1 - 贡献概述

贡献到任何 Dapr 项目仓库的通用指南

感谢您对 Dapr 的关注! 本文档提供了如何通过 issue 和 pull request 为 Dapr 项目 做出贡献的指南。您还可以通过其他方式做出贡献,例如参与社区电话会议、评论 issue 或 pull request 等。

有关社区参与和社区成员资格的更多信息,请参阅 Dapr 社区仓库

Dapr 仓库索引

以下是 Dapr 组织下您可以做出贡献的仓库列表:

  1. Docs:此仓库包含 Dapr 的文档。您可以通过更新现有文档、修复错误或添加新内容来做出贡献,以改善用户体验和清晰度。请参阅文档贡献的具体指南。

  2. Quickstarts:Quickstarts 仓库提供简单、分步指南,帮助用户快速入门 Dapr。此仓库的贡献包括创建新的快速入门指南、改进现有指南,或确保它们与最新功能保持同步。

  3. Runtime:Dapr runtime 仓库包含核心运行时组件。在这里,您可以通过修复 bug、优化性能、实现新功能或增强现有功能来做出贡献。

  4. Components-contrib:此仓库托管了社区为 Dapr 贡献的组件集合。您可以通过添加新组件、改进现有组件或审查和测试社区贡献来做出贡献。

  5. SDKs:Dapr SDK 为各种编程语言提供与 Dapr 交互的库。您可以通过改进 SDK 功能、修复 bug 或添加对新功能的支持来做出贡献。请参阅特定 SDK 的贡献指南

  6. CLI:Dapr CLI 在本地开发机器或 Kubernetes 集群上设置 Dapr,用于启动和管理 Dapr 实例。对 CLI 仓库的贡献包括添加新功能、修复 bug、改善可用性,以及确保与最新 Dapr 版本的兼容性。请参阅开发指南以帮助您开始开发 Dapr CLI。

Issues

Issue 类型

在大多数 Dapr 仓库中,通常有 4 种类型的 issue:

  • Issue/Bug:您在代码中发现了一个 bug,想要报告它,或创建一个 issue 来跟踪该 bug。
  • Issue/Discussion:您有一些想法,需要其他人通过讨论提供意见,最终形成提案。
  • Issue/Proposal:用于提出新想法或功能的项目。这允许在编写代码之前获得他人的反馈。
  • Issue/Question:如果您需要帮助或有疑问,请使用此 issue 类型。

提交之前

在提交 issue 之前,请确保您已检查以下内容:

  1. 是否选择了正确的仓库?
    • Dapr 项目分布在多个仓库中。如果您不确定哪个仓库是正确的,请查看仓库列表
  2. 检查现有 issue
    • 在创建新 issue 之前,请在开放 issue中进行搜索,查看是否已提交了该 issue 或功能请求。
    • 如果您发现您的 issue 已存在,请发表相关评论并添加您的反应。使用反应:
      • 👍 赞成
      • 👎 反对
  3. 对于 bug
    • 检查这是否是环境问题。例如,如果在 Kubernetes 上运行,请确保已满足先决条件(状态存储、绑定等)。
    • 您拥有尽可能多的数据。这通常采用日志和/或堆栈跟踪的形式。如果在 Kubernetes 或其他环境中运行,请查看 Dapr 服务(运行时、operator、placement 服务)的日志。有关如何获取日志的更多详细信息,请参见此处
  4. 对于提案
    • 对 Dapr 运行时的许多更改可能需要更改 API。在这种情况下,讨论潜在功能的最佳位置是主 Dapr 仓库
    • 其他示例可能包括绑定、状态存储或全新的组件。

Pull Requests

所有贡献都通过 pull requests 提交。要提交建议的更改,请遵循此工作流程:

  1. 确保已提出 issue(bug 或提案),为您即将做出的贡献设定预期。
  2. Fork 相关仓库并创建新分支
    • 某些 Dapr 仓库支持 Codespaces为您提供即时环境来构建和测试您的更改。
    • 有关设置 Dapr 开发环境的更多信息,请参阅开发 Dapr 文档
  3. 创建您的更改
    • 代码更改需要测试
  4. 更改的相关文档
  5. 使用 DCO 签署提交并打开 PR
  6. 等待 CI 过程完成,确保所有检查都通过
  7. 将分配一名项目维护者,您可以在几天内收到审查

使用进行中的 PR 获取早期反馈

在投入太多时间之前进行交流的一个好方法是创建一个"进行中"的 PR 并与您的审查者分享。执行此操作的标准方法是在 PR 标题中添加"[WIP]“前缀并分配 do-not-merge 标签。这将让查看您的 PR 的人知道它还不够完善。

第三方代码的使用

  • 第三方代码必须包含许可证。

开发者来源证书:签署您的工作

每个提交都需要签署

开发者来源证书 (DCO) 是贡献者证明他们编写或有权提交他们贡献给项目的代码的一种轻量级方式。以下是完整的 DCO文本,为便于阅读而重新格式化:

By making a contribution to this project, I certify that:
    (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or
    (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or
    (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it.
    (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved.

贡献者通过在提交消息中添加 Signed-off-by 行来签署他们遵守这些要求。

This is my commit message
Signed-off-by: Random J Developer <random@developer.example.org>

Git 甚至有一个 -s 命令行选项可以自动将其附加到您的提交消息中:

$ git commit -s -m 'This is my commit message'

每个 Pull Request 都会检查 Pull Request 中的提交是否包含有效的 Signed-off-by 行。

我没有签署我的提交,现在怎么办?!

别担心 - 您可以轻松重播您的更改、签署它们并强制推送!

git checkout <branch-name>
git commit --amend --no-edit --signoff
git push --force-with-lease <remote-name> <branch-name>

行为准则

请参阅 Dapr 社区行为准则

2 - 发表关于 Dapr 的演讲

如何发表关于 Dapr 的演讲及示例

我们鼓励社区成员发表关于 Dapr 的演讲。为了帮助您快速入门,我们提供三个 PowerPoint 文件:

  • dapr-slidedeck.pptx,这是一个 150 多页的幻灯片,包含:Dapr 概述、所有构建块 API、横切关注点、托管选项以及用于创建自己架构图的资产。
  • dapr-workflow-slidedeck.pptx,这是一个关于 Dapr Workflow 的专用幻灯片,包含:持久执行概念、workflow 编写、workflow 模式、workflow 管理和挑战与技巧。
  • dapr-agents-slidedeck.pptx,这是一个关于 Dapr Agents 的专用幻灯片,包含:AI agents 解释、Dapr Agent 类型、多智能体系统和智能体模式。

有一个可下载的 zip 文件包含所有幻灯片。

下载 Dapr 演示文稿套件

发表 Dapr 演讲

  • 首先下载 Dapr 演示文稿套件。其中包含幻灯片、图表和图形资产。
  • 然后查阅文档以确保您理解概念
  • 使用 Dapr 快速入门 仓库来演示如何使用 Dapr。
  • 完成 Dapr 演讲后,通过将您的演讲添加到 Dapr 社区仓库中的此表格来申领 Dapr Presenter 徽章。

来自社区的 Dapr 演讲

如果您需要一些灵感,请通过此 Dapr YouTube 播放列表 观看社区成员的 Dapr 演讲:

其他资源

社区 仓库中有其他 Dapr 资源。

3 - Dapr 路线图

Dapr 路线图让社区能够了解项目的不同优先级

请参阅此文档查看 Dapr 项目的路线图。

4 - 使用 GitHub Codespaces 贡献

如何使用 GitHub Codespaces 为 Dapr 项目做出贡献

GitHub Codespaces 是为 Dapr 仓库做贡献最简单的方式。只需轻轻一点,你就可以在浏览器中拥有一个包含所有前置条件的就绪环境。

功能特性

  • 点击即运行:获得一个专用的沙箱环境,其中包含所有必需的框架和包,即刻可用。
  • 按使用量计费:只需为你在 Codespaces 中开发的时间付费。环境在不使用时会自动关闭。
  • 便携性:在浏览器或 Visual Studio Code 中运行,或使用 SSH 连接到它。

在 Codespace 中打开 Dapr 仓库

要在 Codespace 中打开 Dapr 仓库,请从仓库主页选择 “Code”,然后选择 “Open with Codespaces”:

Screenshot of creating a Dapr Codespace

如果你还没有 fork 该仓库,创建 Codespace 也会自动为你创建一个 fork,并在 Codespace 中使用它。

支持的仓库

在 Codespace 中开发 Dapr 组件

开发新的 Dapr 组件需要同时使用 dapr/components-contribdapr/dapr 这两个仓库。建议将这两个文件夹并排放置在 /workspaces 目录下。

如果你从 dapr/dapr 创建了 Codespace

如果你的 Codespace 是从 dapr/dapr 仓库或其 fork 启动的,你需要在 /workspaces/components-contrib 中克隆 dapr/components-contrib 仓库(或你的 fork)。

首先,确保你已通过 GitHub CLI 完成身份验证:

# 运行此命令并按照提示操作
# 大多数用户应接受默认选项
gh auth login

克隆仓库:

# 如果你想使用自己的 dapr/components-contrib fork,请将其替换为你的 fork(例如 "yourusername/components-contrib")
# 确保在执行此操作前你已经 fork 了该仓库
REPO=dapr/components-contrib
cd /workspaces
gh repo clone "$REPO" /workspaces/components-contrib

然后,将该文件夹添加到当前工作区:

code -a /workspaces/components-contrib

如果你从 dapr/components-contrib 创建了 Codespace

如果你的 Codespace 是从 dapr/components-contrib 仓库或其 fork 启动的,你需要在 /workspaces/dapr 中克隆 dapr/dapr 仓库(或你的 fork)。

首先,确保你已通过 GitHub CLI 完成身份验证:

# 运行此命令并按照提示操作
# 大多数用户应接受默认选项
gh auth login

克隆仓库:

# 如果你想使用自己的 dapr/dapr fork,请将其替换为你的 fork(例如 "yourusername/dapr")
# 确保在执行此操作前你已经 fork 了该仓库
REPO=dapr/dapr
cd /workspaces
gh repo clone "$REPO" /workspaces/dapr

然后,将该文件夹添加到当前工作区:

code -a /workspaces/dapr

相关链接

5 - Dapr bot 参考

Dapr bot 功能列表。

Dapr bot 由一系列命令触发,用于协助 Dapr 组织中的常见任务。它会为每个仓库单独设置(示例),可以配置为在特定事件上运行。以下是命令列表以及实现这些命令的仓库列表。

命令参考

命令目标描述谁可以使用仓库
/assignIssue将 issue 分配给用户或用户组任何人daprdocsquickstartsclicomponents-contribgo-sdkjs-sdkjava-sdkpython-sdkdotnet-sdkrust-sdk
/ok-to-testPull requestdapr:触发端到端测试
components-contrib:触发 conformance 和 certification tests
bot 中列出的用户daprcomponents-contrib
/ok-to-perfPull request触发性能测试。bot 中列出的用户dapr
/make-me-laughIssue 或 pull request发布一个随机笑话bot 中列出的用户daprcomponents-contrib

标签参考

你可以使用 created-by/dapr-bot 标签查询 Dapr bot 创建的 issue(查询)。

标签目标作用仓库
docs-neededIssuedapr/docs 中创建新 issue 以跟踪文档工作dapr
sdk-neededIssue跨 SDK 仓库创建新 issue 以跟踪 SDK 工作dapr
documentation requiredIssue 或 pull requestdapr/docs 中创建新 issue 以跟踪文档工作components-contrib
new componentIssue 或 pull requestdapr/dapr 中创建新 issue 以注册新组件components-contrib

6 - SDK 贡献指南

如何为 Dapr SDK 文档做出贡献

6.1 - 为 .NET SDK 做贡献

为 Dapr .NET SDK 做贡献的指南

欢迎!

如果您正在阅读本文,您可能有兴趣为 Dapr 和/或 Dapr .NET SDK 做贡献。欢迎加入本项目,感谢您对贡献的兴趣!

请查阅相关文档,熟悉 Dapr 是什么以及它要实现的目标,并通过 Discord 与我们联系。请告诉我们您希望如何贡献,我们很乐意提供想法和建议。

为 Dapr 做贡献有多种方式:

如果您是代码库的新手,请随时在 Discord 的 #dotnet-sdk 频道询问如何实现更改或提出一般性问题。您不需要获得许可即可处理任何问题,但请注意,如果某个问题已分配给某人,这表明可能有人已开始处理该问题。特别是如果该问题已有一段时间没有活动,请随时联系,确认他们是否仍有兴趣继续,或者您是否可以接手,并使用您的实现打开一个拉取请求。

如果您想将自己分配给某个问题,请使用 “/assign” 回复对话,机器人会将您分配给它。

我们已将一些问题标记为 good-first-issuehelp wanted,表明这些可能是小型的、自包含的更改。

如果您对实现不确定,请将其创建为草稿拉取请求,并通过标记 @dapr/maintainers-dotnet-sdk 并提供一些您需要帮助的上下文来征求 .NET 维护者 的反馈。

贡献规则和最佳实践

.NET SDK 做贡献时,应遵循以下规则和最佳实践。

拉取请求

通常不鼓励仅包含格式更改的拉取请求。拉取请求应旨在修复 bug、添加新功能或改进现有功能。

请尽量将拉取请求的内容最小化,使其仅涵盖单个问题。涉及大量文件的大型 PR 不太可能在短时间内被审查或接受。在单个 PR 中处理许多不同问题使得难以确定您的代码是否完全解决了潜在问题,并使代码审查变得复杂。

测试

所有拉取请求都应包含单元测试和/或集成测试,以反映添加或更改内容的性质,以便清楚地表明功能按预期工作。避免使用自动生成的测试来多次重复测试相同的功能。相反,应通过验证更改的每个可能路径来提高代码覆盖率,以便未来的贡献者可以更容易地理解您逻辑的轮廓并更容易地识别局限性。

示例

examples 目录包含代码示例,供用户运行以尝试各种 Dapr .NET SDK 包和扩展的特定功能。在编写和更新示例时,请记住:

  • 所有示例都应能够在 Windows、Linux 和 MacOS 上运行。虽然 .NET Core 代码在操作系统之间保持一致,但任何示例前/后命令都应通过 tabpane 提供选项
  • 包含下载/安装任何所需先决条件的步骤。对于全新操作系统安装的人来说,应该能够从示例开始并完成而不会出错。指向外部下载页面的链接是可以的。

文档

daprdocs 目录包含呈现到 Dapr Docs 网站的 markdown 文件。当构建文档网站时,会克隆此仓库并进行配置,使其内容与文档内容一起呈现。在编写文档时,请记住:

  • 除了这些规则外,还应遵循 docs 指南 中的所有规则。
  • 所有文件和目录都应以 dotnet- 为前缀,以确保所有文件/目录名称在所有 Dapr 文档中全局唯一。

所有拉取请求都应努力在代码中包含 XML 文档,清楚地说明功能的作用以及存在的原因,以及对已发布文档的更改,以便为其他开发人员阐明您的更改如何改进 Dapr 框架。

GitHub Dapr Bot 命令

查看 daprbot 文档,了解您可以在该仓库中运行的常见任务的 GitHub 命令。例如,您可以在问题上评论 /assign 将其分配给自己。

提交签名

提交到 Dapr .NET SDK 的所有代码必须由编写它的开发者签名。这意味着每个提交都必须以以下内容结尾:

Signed-off-by: First Last flast@example.com

姓名和电子邮件地址必须与提交更改的用户的注册 GitHub 姓名和电子邮件地址匹配。我们使用机器人在拉取请求中检测这一点,如果此检查无法验证,我们将无法合并 PR。

如果您注意到 PR 由于早期 PR 历史中的 DCO 检查失败而未能验证,请考虑在本地压缩 PR 并重新提交,以确保签名声明包含在提交历史中。

语言、工具和流程

Dapr .NET SDK 中的所有源代码都是用 C# 编写的,并以最早支持的 .NET SDK 可用的最新语言版本为目标。截至 v1.16,这意味着同时支持 .NET 8 和 .NET 9。可用的最新语言版本是 C# version 12

贡献者欢迎使用他们最舒适开发的任何 IDE,但请不要随您的贡献提交 IDE 特定的偏好文件,因为这些文件将被拒绝。

6.2 - 为 Go SDK 做贡献

为 Dapr Go SDK 做贡献的指南

在为 Go SDK 做贡献时,应遵循以下规则和最佳实践。

示例

examples 目录包含代码示例,供用户运行以试用各种 Go SDK 包和扩展的特定功能。在编写新的和更新的示例时,请记住:

  • 所有示例都应能在 Windows、Linux 和 MacOS 上运行。虽然 Go 代码在各个操作系统之间保持一致,但任何示例前/后命令都应通过 tabpane 提供选项
  • 应包含下载/安装任何所需先决条件的步骤。对于全新操作系统安装的用户,应该能够从示例开始并无错误地完成。链接到外部下载页面也是可以的。

文档

daprdocs 目录包含 markdown 文件,这些文件被渲染到 Dapr 文档 网站中。构建文档网站时,会克隆此仓库并对其进行配置,使其内容与文档内容一起渲染。在编写文档时,请记住:

  • 除了这些规则外,还应遵循 文档指南 中的所有规则
  • 所有文件和目录都应以 go- 为前缀,以确保所有文件/目录名称在所有 Dapr 文档中都是全局唯一的

6.3 - 为 Java SDK 贡献

为 Dapr Java SDK 贡献的指南

在为 Java SDK 贡献时,应遵循以下规则和最佳实践。

示例

examples 目录包含代码示例,供用户运行以试用各种 Java SDK 包和扩展的特定功能。在编写和更新示例时,请记住:

  • 所有示例都应能在 Windows、Linux 和 MacOS 上运行。虽然 Java 代码在不同操作系统之间是一致的,但任何示例前后的命令都应通过 tabpane 提供选项
  • 包含下载/安装任何所需先决条件的步骤。从全新操作系统安装开始的人应该能够开始并完成该示例而不会出错。指向外部下载页面的链接是可以的。

文档

daprdocs 目录包含 markdown 文件,这些文件被渲染到 Dapr Docs 网站上。构建文档网站时,此仓库被克隆并配置,使其内容与文档内容一起渲染。在编写文档时,请记住:

  • 除了这些规则外,还应遵循文档指南中的所有规则
  • 所有文件和目录应以 java- 为前缀,以确保所有文件/目录名称在所有 Dapr 文档中全局唯一

Github Dapr Bot 命令

查看 daprbot 文档,了解你可以在此仓库中运行的常见任务的 Github 命令。例如,你可以运行 /assign(作为问题评论)将问题分配给自己。

6.4 - 为 JavaScript SDK 做出贡献

为 Dapr JavaScript SDK 做出贡献的指南

在为 JavaScript SDK 做出贡献时,应遵循以下规则和最佳实践。

💡 您可以运行 npm pretty-fix 来对所有文件运行 prettier

提交指南

Dapr Javascript SDK 使用 Conventional Commits 规范。自动变更日志工具使用这些规范基于提交消息自动生成变更日志。以下是编写提交消息的指南, 以支持此功能:

格式

type(scope)!: subject
  • type:提交类型是以下之一:

    • feat:新功能。
    • fix:bug 修复。
    • docs:文档变更。
    • refactor:特定代码部分的重构,不引入新功能或 bug 修复。
    • style:代码风格改进。
    • perf:性能改进。
    • test:测试套件的变更。
    • ci:CI 系统的变更。
    • build:构建系统的变更(我们还没有构建系统,所以这不适用)。
    • chore:其他不符合上述类型的变更。这不会出现在变更日志中。
  • scope:提交所更改的代码库部分。如果它更改了多个部分,或者没有修改特定部分,则留空不使用括号。 示例:

    • 添加 test 的提交:
    test(actors): add an actor test
    
    • 一次性更改多项的提交:
    style: adopt eslint
    

    对于示例的更改,scope 应该是示例名称加上 examples/ 前缀:

    • fix(agnoster): commit subject
    • fix(examples/http/actor): commit subject
  • !:放在 scope 之后(如果 scope 为空,则放在 type 之后),表示该提交引入了破坏性变更。

    可选地,您可以指定一条消息,变更日志工具将向用户显示该消息,以指示更改内容以及他们可以如何处理。 您可以使用多行来输入此消息;变更日志解析器将继续读取,直到提交消息结束或遇到空行。

    示例(虚构):

    style(agnoster)!: change dirty git repo glyph
    
    BREAKING CHANGE: the glyph to indicate when a git repository is dirty has
    changed from a Powerline character to a standard UTF-8 emoji.
    
    Fixes #420
    
    Co-authored-by: Username <email>
    
  • subject:更改的简要描述。这将显示在变更日志中。如果您需要指定其他详细信息,可以使用提交正文, 但这些内容不会可见。

    格式技巧:提交主题可能包含:

    • 通过编写 #issue 来链接相关问题或 PR。变更日志工具将突出显示这些内容:

      feat(archlinux): add support for aura AUR helper (#9467)
      
    • 通过使用反引号来格式化内联代码:反引号之间的文本也会被变更日志工具突出显示:

      feat(shell-proxy): enable unexported `DEFAULT_PROXY` setting (#9774)
      

风格

尽量保持第一行提交内容简短。使用此提交风格很难做到这一点,但要尽量简洁, 如果需要更多空间,可以使用提交正文。尽量确保提交主题清晰准确,使用户仅通过查看变更日志就能了解更改内容。

Github Dapr Bot 命令

查看 daprbot 文档,了解您可以在此仓库中运行的用于常见任务的 Github 命令。 例如,您可以运行 /assign(作为问题的评论)来将问题分配给用户或用户组。

编码规则

为确保源代码的一致性,在工作时请记住这些规则:

  • 所有功能或 bug 修复必须通过一个或多个规范(单元测试)进行测试
  • 所有公共 API 方法必须记录文档
  • 我们遵循 ESLint 推荐规则

示例

examples 目录包含代码示例,供用户运行以试用各种 JavaScript SDK 包和扩展的特定功能。 在编写新的和更新的示例时,请记住:

  • 所有示例应该能够在 Windows、Linux 和 MacOS 上运行。虽然 JavaScript 代码在操作系统之间是一致的, 但任何示例前/后命令应该通过 tabpane 提供选项。
  • 包含下载/安装任何必要先决条件的步骤。从全新操作系统安装开始的人应该能够开始示例并完成它而不会出错。 指向外部下载页面的链接是可以的。

文档

daprdocs 目录包含 markdown 文件,这些文件被渲染到 Dapr 文档 网站。 构建文档网站时,此仓库被克隆并配置,使其内容与文档内容一起渲染。在编写文档时,请记住:

  • 除了这些规则外,还应遵循文档指南 中的所有规则。
  • 所有文件和目录应以 js- 为前缀,以确保所有文件/目录名称在所有 Dapr 文档中都是全局唯一的。

6.5 - 为 Python SDK 贡献

关于为 Dapr Python SDK 贡献的指南

当为 Python SDK 贡献时,应遵循以下规则和最佳实践。

示例

examples 目录包含代码示例,供用户运行以尝试各种 Python SDK 包和扩展的特定功能。在编写新的和更新的示例时,请注意:

  • 所有示例都应能在 Windows、Linux 和 MacOS 上运行。虽然 Python 代码在操作系统之间保持一致,但任何示例前/后的命令应通过 tabpane 提供选项
  • 包含下载/安装任何所需先决条件的步骤。对于一个全新操作系统安装的用户来说,应该能够开始示例并完成它而不会出现错误。指向外部下载页面的链接是可以的。

文档

daprdocs 目录包含渲染到 Dapr 文档 网站的 markdown 文件。当构建文档网站时,该仓库会被克隆并配置,使其内容与文档内容一起渲染。在编写文档时,请注意:

  • 除了这些规则外,还应遵循 文档指南 中的所有规则
  • 所有文件和目录应以 python- 为前缀,以确保所有文件/目录名称在所有 Dapr 文档中全局唯一

Github Dapr Bot 命令

查看 daprbot 文档,了解您可以在此仓库中运行的常见任务的 GitHub 命令。例如,您可以运行 /assign(作为问题的评论)将问题分配给用户或用户组。

6.6 - 为 Rust SDK 做贡献

为 Dapr Rust SDK 做贡献的指南

在为 Rust SDK 做贡献时,应遵循以下规则和最佳实践。

示例

examples 目录包含代码示例,供用户运行以体验各种 Rust SDK 包和扩展的特定功能。它还托管用于验证的组件示例。在编写和更新示例时,请牢记:

  • 所有示例应能在 Windows、Linux 和 MacOS 上运行。虽然 Rust 代码在操作系统之间是一致的,除了少数操作系统功能特性需要条件编译,但任何示例前/后命令都应通过 tabpane 提供选项
  • 包含下载/安装任何必需先决条件的步骤。刚安装好操作系统的人应该能够开始示例并顺利完成,不会出现错误。链接到外部下载页面是可以的。
  • 示例应通过验证,并包含机械化的 markdown 步骤,并添加到验证工作流中 待定

文档

daprdocs 目录包含渲染到 Dapr 文档 网站的 markdown 文件。当构建文档网站时,此仓库会被克隆并配置,使其内容与文档内容一起渲染。在编写文档时,请牢记:

  • 除了这些规则外,还应遵循文档指南中的所有规则。
  • 所有文件和目录应以 rust- 为前缀,以确保所有文件/目录名称在所有 Dapr 文档中全局唯一。

更新 Protobuf

要从 dapr/dapr 仓库拉取 protobuf,可以运行仓库根目录中的脚本,如下所示:

./update-protos.sh

默认情况下,脚本从 Dapr 仓库的 master 分支获取最新的 proto 更新。如果需要选择特定的发行版本,使用 -v 标志:

./update-protos.sh -v v1.13.0

7 - 文档贡献指南

如何为 Dapr 文档做出贡献

7.1 - 贡献者指南

开始为 Dapr 文档做贡献

在本指南中,你将学习如何为 Dapr 文档仓库 贡献内容。由于 Dapr 文档发布在 docs.dapr.io 上,你必须确保你的贡献能够正确编译和发布。

前置条件

在为 Dapr 文档做贡献之前:

分支指南

Dapr 文档处理分支的方式与大多数代码仓库不同。它不使用 main 分支,而是每个分支都标记为与运行时发布的主版本和次版本相匹配。完整列表请访问 Docs 仓库

一般来说,所有文档更新都应指向 Dapr 最新发布的文档分支。最新发布是默认分支 [https://github.com/dapr/docs]。例如,如果你要修复拼写错误、添加注释或澄清某个观点,请将你的更改提交到默认的 Dapr 分支。

对于适用于候选版本或预发布版本文档的任何文档更改,请将你的更改指向该特定分支。例如,如果你要记录即将对某个组件或运行时进行的更改,请将你的更改提交到预发布分支。

风格和语气

风格和语气约定应贯穿所有 Dapr 文档,以保持文档的一致性:

风格/语气指导原则
大小写仅使用大写:
  • 句子或标题开头
  • 专有名词,包括技术名称(Dapr、Redis、Kubernetes 等)
标题和标题标题必须简短,但描述性强且清晰。
使用简单的句子编写易于阅读和浏览的句子。提示:去掉正式语气,就像直接与读者交谈一样。
避免使用第一人称不要使用第一人称 “我”、“我们” 和 “我们的”,而应使用第二人称 “你” 和 “你的”。
假设读者是"新开发者"对有经验的开发者来说显而易见的步骤,对新开发者可能并不那么明显。为读者提供更明确、更详细的说明。
使用现在时避免使用 “此命令 安装 Redis” 这样的句子。而应使用 “此命令安装 Redis”。

图表和图像

图表和图像是文档页面的宝贵视觉辅助工具。使用 Dapr 图表模板库 中的图表风格和图标。

为文档创建图表的流程:

  1. 下载 Dapr 图表模板库,使用其中的图标和颜色。
  2. 添加新幻灯片并创建你的图表。
  3. 将图表截取为高分辨率 PNG 文件,保存到 images 文件夹
  4. 使用概念或构建块的命名约定来命名 PNG 文件,以便它们归为一组。
    • 例如:service-invocation-overview.png
    • 有关使用 shortcode 调用图像的更多信息,请参阅下面的 图像指南
  5. 使用 HTML <image> 标签将图表添加到文档的适当部分。
  6. 在你的 PR 中,评论图表幻灯片(不是截图),以便维护者可以审核并将其添加到图表库中。

贡献新的文档页面

如果你要创建新文章,请确保:

  • 将新文档放置在层次结构的正确位置。
  • 避免创建新部分。大多数情况下,正确位置已在文档层次结构中。
  • 包含完整的 Hugo front-matter

选择下面的主题类型,查看建议的模板以帮助你开始。

主题类型它是什么?
概念回答"这能帮我解决什么问题?“避免重复 API 或组件规范;提供更多细节。
快速入门提供"五分钟让你惊艳"的体验。快速引导读者了解某个功能或 API 及其在受控示例中的工作方式。
操作指南提供详细的、实用的 Dapr 功能或技术分步指南。鼓励读者尝试自己的场景,而不是快速入门中提供的受控场景。

docs.dapr.io 的要求

确保你的贡献不会破坏网站构建。Hugo 构建网站的方式要求遵循以下指南:

文件和文件夹名称

文件和文件夹名称应全局唯一。 - \service-invocation - service-invocation-overview.md

Front-matter

Front-matter 是将常规 markdown 文件升级为 Hugo 兼容文档的要素,用于在导航栏和目录中呈现。

每个页面都需要在文档顶部包含如下部分:

---
type: docs
title: "PAGE TITLE"
linkTitle: "NAV BAR SHORT TITLE"
weight: (number)
description: "1+ SENTENCES DESCRIBING THE ARTICLE"
---

示例

---
type: docs
title: "Service invocation overview"
linkTitle: "Overview"
weight: 10
description: "A quick overview of Dapr service invocation and how to use it to invoke services within your application"
---

Weight 决定页面在左侧边栏中的顺序,0 为最顶部。

Front-matter 应包含所有字段:type、title、linkTitle、weight 和 description。

  • title 应为一句话,末尾无句号
  • linkTitle 应为 1-3 个单词,操作指南除外。
  • description 应为 1-2 句话,描述读者将在本文档中学到、完成或做什么。

根据 样式约定,标题应仅首字母和专有名词大写,“How-To:” 除外:

  • “Getting started with Dapr service invocation”
  • “How-To: Setup a local Redis instance”

引用其他页面

Hugo refrelref shortcodes 用于引用其他页面和部分。这些 shortcodes 还允许在页面被错误重命名或删除时中断构建。

例如,这个 shortcode,与 markdown 页面的其余内容一起内联编写,将链接到 section/folder 名称的 _index.md:

{{% ref "folder" %}}

而这个 shortcode 将链接到特定页面:

{{% ref "page" %}}

所有页面和文件夹都需要具有_全局唯一名称_,以使 ref shortcode 正常工作。如果有重复名称,构建将中断并抛出错误。

引用其他页面中的部分

要引用另一个页面中的特定部分,请在引用末尾添加 #section-short-name

通常,section short name 是 section 标题的文本,全部小写,空格改为 “-"。你可以通过以下方式检查 section short name:

  1. 访问网站页面。
  2. 点击 section 旁边的链接图标(🔗)。
  3. 查看导航栏中 URL 的呈现方式。
  4. 复制 “#” 之后的内容作为你的 section shortname。

例如,对于这个特定部分,页面和部分的完整引用为:

{{% ref "contributing-docs#referencing-sections-in-other-pages" %}}

Shortcodes

以下是对 Dapr 文档编写有用的 shortcodes

图像

Docsy 和 Hugo 使用的 markdown 规范不支持使用 markdown 符号调整图像大小。而是使用原始 HTML。

首先将图像放置在 /daprdocs/static/images 下,命名约定为 [page-name]-[image-name].[png|jpg|svg]

然后使用以下方式链接图像:

<img src="/images/[image-filename]" width=1000 alt="Description of image">

不要忘记设置 alt 属性以保持文档可读性和可访问性。

示例

此 HTML 将在 overview.md 页面上显示 dapr-overview.png 图像:

<img src="/images/overview-dapr-overview.png" width=1000 alt="Overview diagram of Dapr and its building blocks">

选项卡内容

选项卡通过 Hugo shortcodes 实现。

整体格式为:









[Tab1 的内容]

[Tab2 的内容]

你创作的所有内容都将呈现为 markdown,因此你可以包含图像、代码块、YouTube 视频等。

示例











powershell -Command "iwr -useb https://raw.githubusercontent.com/dapr/cli/master/install/install.ps1 | iex"
wget -q https://raw.githubusercontent.com/dapr/cli/master/install/install.sh -O - | /bin/bash
brew install dapr/tap/dapr-cli

此示例将呈现为:

powershell -Command "iwr -useb https://raw.githubusercontent.com/dapr/cli/master/install/install.ps1 | iex"
wget -q https://raw.githubusercontent.com/dapr/cli/master/install/install.sh -O - | /bin/bash
brew install dapr/tap/dapr-cli

YouTube 视频

Hugo 可以使用 shortcode 自动嵌入 YouTube 视频:

{{% youtube [VIDEO ID] %}}

示例

给定视频 https://youtu.be/dQw4w9WgXcQ

shortcode 为:

{{% youtube dQw4w9WgXcQ %}}

按钮

要在网页中创建按钮,请使用 button shortcode。

可选的 “newtab” 参数指示页面是否应在新标签页中打开。选项为 “true” 或 “false”。默认为 “false”,即页面将在同一标签页中打开。

链接到外部页面

{{% button text="My Button" link="https://example.com" %}}
My Button

链接到其他文档页面

你也可以在按钮中引用页面:

{{% button text="My Button" page="contributing" newtab="true" %}}
My Button

按钮颜色

你可以使用 Bootstrap 颜色自定义颜色:

{{% button text="My Button" link="https://example.com" color="primary" %}}
{{% button text="My Button" link="https://example.com" color="secondary" %}}
{{% button text="My Button" link="https://example.com" color="success" %}}
{{% button text="My Button" link="https://example.com" color="danger" %}}
{{% button text="My Button" link="https://example.com" color="warning" %}}
{{% button text="My Button" link="https://example.com" color="info" %}}

My Button

My Button

My Button

My Button

My Button

My Button

参考资料

Docsy 编写指南

翻译

Dapr 文档支持使用 git 子模块和 Hugo 内置的语言支持向文档添加语言翻译。

你可以在 PR 1286 中找到添加中文语言支持的示例 PR。

添加语言的步骤:

  • 在 Docs 仓库中开一个 issue,请求创建新的语言特定文档仓库

  • 创建后,在文档仓库中创建 git 子模块:

    git submodule add <remote_url> translations/<language_code>
    
  • daprdocs/config.toml 中添加语言条目:

     [languages.<language_code>]
       title = "Dapr Docs"
       weight = 3
       contentDir = "content/<language_code>"
       languageName = "<language_name>"
    
  • daprdocs/config.toml 中创建挂载:

    [[module.mounts]]
      source = "../translations/docs-<language_code>/content/<language_code>"
      target = "content"
      lang = "<language_code>"
    
  • 根据需要为所有其他翻译目录重复上述步骤。

下一步

从复制 Dapr 文档模板 开始工作。

7.2 - 维护者指南

开始成为 Dapr 文档维护者和审批者。

在本指南中,您将学习如何执行日常的 Dapr 文档维护者和审批者职责。为了成功完成这些任务,您需要在 dapr/docs 仓库中拥有审批者或维护者身份。

要了解如何为 Dapr 文档做出贡献,请参阅贡献者指南

分支指南

Dapr 文档的分支处理方式与大多数代码仓库不同。它没有 main 分支,每个分支都标记为与运行时发布的主版本号和次版本号相匹配。

有关完整列表,请访问 Docs 仓库

阅读贡献者指南以了解有关发布分支的更多信息。

从当前发布分支向上合并到预发布分支

作为文档审批者或维护者,您需要执行常规向上合并,以保持预发布分支与当前发布分支的更新保持一致。建议每周将当前分支向上合并到预发布分支中一次。

在以下步骤中,将 v1.0 视为当前发布版本,将 v1.1 视为即将发布的版本。

  1. 打开 Visual Studio Code 并切换到 Dapr 文档仓库。

  2. 在本地仓库中,切换到最新分支(v1.0)并同步更改:

    git pull upstream v1.0
    git push origin v1.0
    
  3. 切换到即将发布的分支(v1.1)并同步更改:

    git pull upstream v1.1
    git push origin v1.1
    
  4. 基于即将发布的版本创建一个新分支:

    git checkout -b upmerge_MM-DD
    
  5. 打开终端,暂存从最新发布版本到向上合并分支的合并:

    git merge --no-ff --no-commit v1.0
    
  6. 在终端中,确保包含的文件看起来准确无误。在 VS Code 中检查任何合并冲突。删除不需要合并的配置更改或版本信息。

  7. 提交暂存的更改并推送到向上合并分支(upmerge_MM-DD)。

  8. 从向上合并分支向即将发布的分支(v1.1)打开一个 PR。

  9. 审查该 PR 并仔细检查没有将非预期的更改推送到向上合并分支。

发布流程

Dapr 文档必须与 Dapr 项目发布中包含的功能和更新保持一致。在 Dapr 发布日期之前,请确保:

  • 所有新功能或更新都已充分记录并经过审查。
  • 即将发布的文档 PR 指向发布分支。

在以下步骤中,将 v1.0 视为最新发布版本,将 v1.1 视为即将发布的版本。

文档的发布流程需要以下内容:

  • 将最新发布版本向上合并到即将发布的分支
  • 更新最新和即将发布的 Hugo 配置文件
  • 为下一个版本创建新的 Azure 静态 Web 应用
  • 为下一个版本的网站创建新的 DNS 条目
  • 为下一个版本创建新的 git 分支

向上合并

首先,执行从最新发布版本到即将发布分支的文档向上合并

更新 Hugo 配置

向上合并后,为发布准备文档分支。在两个独立的 PR 中,您需要:

  • 归档最新发布版本。
  • 将预览/发布分支作为文档的当前实时版本。
  • 创建一个新的预览分支。

最新发布版本

这些步骤将为归档准备最新发布分支。

  1. 打开 VS Code 并切换到 Dapr 文档仓库。

  2. 切换到最新分支(v1.0)并同步更改:

    git pull upstream v1.0
    git push origin v1.0
    
  3. 基于最新发布版本创建一个新分支:

    git checkout -b release_v1.0
    
  4. 在 VS Code 中,导航到位于根目录的 hugo.yaml

  5. 将以下配置添加到 # Versioning 部分(大约第 121 行及以后):

    version_menu: "v1.0"
    version: "v1.0"
    archived_version: true
    url_latest_version: "https://docs.dapr.io"
    
    versions:
     - version: v1.2 (preview)
       url: https://v1-2.docs.dapr.io
     - version: v1.1 (latest)
       url: "#"
     - version: v1.0
       url: https://v1-0.docs.dapr.io
    
  6. 删除 .github/workflows/website-root.yml

  7. 提交暂存的更改并推送到您的分支(release_v1.0)。

  8. release_v1.0v1.0 打开一个 PR。

  9. 请文档维护者或审批者进行审查。等待发布后再合并该 PR。

即将发布的版本

这些步骤将为即将发布分支准备提升为最新发布版本。

  1. 打开 VS Code 并切换到 Dapr 文档仓库。

  2. 切换到即将发布的分支(v1.1)并同步更改:

    git pull upstream v1.1
    git push origin v1.1
    
  3. 基于即将发布的版本创建一个新分支:

    git checkout -b release_v1.1
    
  4. 在 VS Code 中,导航到位于根目录的 hugo.yaml

  5. 将第 1 行更新为 baseURL: https://docs.dapr.io/

  6. 更新 # Versioning 部分(大约第 121 行及以后)以显示正确的版本和标签:

    # Versioning
    version_menu: "v1.1 (latest)"
    version: "v1.1"
    archived_version: false
    url_latest_version: https://docs.dapr.io
    github_branch: v1.1
    
    versions:
     - version: v1.2 (preview)
       url: https://v1-2.docs.dapr.io
     - version: v1.1 (latest)
       url: "#"
     - version: v1.0
       url: https://v1-0.docs.dapr.io
    
  7. 导航到 .github/workflows/website-root.yml

  8. 更新触发工作流的分支:

    name: Azure Static Web App Root
    
    on:
      push:
        branches:
          - v1.1
      pull_request:
        types: [opened, synchronize, reopened, closed]
        branches:
          - v1.1
    
  9. 导航到 /README.md

  10. 更新版本表:

| Branch                                                       | Website                    | Description                                                                                      |
| ------------------------------------------------------------ | -------------------------- | ------------------------------------------------------------------------------------------------ |
| [v1.1](https://github.com/dapr/docs) (primary)               | https://docs.dapr.io       | Latest Dapr release documentation. Typo fixes, clarifications, and most documentation goes here. |
| [v1.2](https://github.com/dapr/docs/tree/v1.2) (pre-release) | https://v1-2.docs.dapr.io/ | Pre-release documentation. Doc updates that are only applicable to v1.2+ go here.                |
  1. 更新 support-release-policy.md 中的 Supported versions 表;在表顶部添加一行新的运行时和 SDK 版本。将早于 n-2 的发布版本更改为 Unsupported
  2. dapr-latest-version.html shortcode partial 更新为新的次版本/补丁版本(在此示例中为 1.1.01.1)。
  3. 提交暂存的更改并推送到您的分支(release_v1.1)。
  4. release/v1.1v1.1 打开一个 PR。
  5. 请文档维护者或审批者进行审查。等待发布后再合并该 PR。

未来预览分支

创建预览分支
  1. 在 GitHub UI 中,选择分支下拉菜单并选择 View all branches
  2. 点击 New branch
  3. New branch name 中,输入预览分支版本号。在此示例中,应该是 v1.2
  4. 选择 v1.1 作为源。
  5. 点击 Create new branch
配置预览分支
  1. 在终端窗口中,导航到 docs 仓库。

  2. 切换到即将发布的分支(v1.1)并同步更改:

    git pull upstream v1.1
    git push origin v1.1
    
  3. 基于 v1.1 创建一个新分支并将其命名为 v1.2

git checkout -b release_v1.1
  1. .github/workflows/website-v1-1.yml 重命名为 .github/workflows/website-v1-2.yml

  2. 在 VS Code 中打开 .github/workflows/website-v1-2.yml 并更新名称、触发器和部署目标为 1.2:

    name: Azure Static Web App v1.2
    
    on:
      push:
        branches:
          - v1.2
      pull_request:
        types: [opened, synchronize, reopened, closed]
        branches:
          - v1.2
    
     ...
    
         with:
           azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_V1_2 }}
           repo_token: ${{ secrets.GITHUB_TOKEN }}
    
     ...
    
         with:
           azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_V1_2 }}
           skip_deploy_on_missing_secrets: true
    
  3. 导航到 daprdocs/config.toml 并更新 baseURL 以指向新的预览网站:

    baseURL = "https://v1-2.docs.dapr.io"
    
  4. 更新 # GitHub Information# Versioning 部分(大约第 148 行)以显示正确的版本和标签:

    # GitHub Information
    github_repo = "https://github.com/dapr/docs"
    github_project_repo = "https://github.com/dapr/dapr"
    github_subdir = "daprdocs"
    github_branch = "v1.2"
    
    # Versioning
    version_menu = "v1.2 (preview)"
    version = "v1.2"
    archived_version = false
    url_latest_version = "https://docs.dapr.io"
    
    [[params.versions]]
      version = "v1.2 (preview)"
      url = "#"
    [[params.versions]]
      version = "v1.1 (latest)"
      url = "https://docs.dapr.io"
    [[params.versions]]
      version = "v1.0"
      url = "https://v1-0.docs.dapr.io"
    
  5. 提交暂存的更改并针对 v1.2 分支推送到一个新的 PR。

  6. 在发布和其他 v1.0v1.1 PR 合并之前,暂缓合并该 PR。

为未来发布创建新网站

接下来,为未来的 Dapr 发布创建一个新网站。为此,您需要:

  • 部署 Azure 静态 Web 应用。
  • 通过 CNCF 请求配置 DNS。

先决条件

  • dapr/docs 仓库中拥有文档维护者身份。
  • 访问活动 Dapr Azure 订阅,具有贡献者或所有者访问权限以创建资源。
  • 在您的机器上安装 Azure Developer CLI
  • 将您自己的 dapr/docs 仓库 fork 克隆到您的机器上。

部署 Azure 静态 Web 应用

为未来的 Dapr 发布部署一个新的 Azure 静态 Web 应用。在此示例中,我们使用 v1.1 作为未来发布版本。

  1. 在终端窗口中,导航到 dapr/docs 目录中的 iac/swa 文件夹。

    cd .github/iac/swa
    
  2. 使用 Dapr Azure 订阅登录 Azure Developer CLI (azd)。

    azd login
    
  3. 在浏览器提示中,验证您以 Dapr 身份登录并完成登录。

  4. 在同一终端中,设置这些环境变量:

    export AZURE_RESOURCE_GROUP=docs-website
    export IDENTITY_RESOURCE_GROUP=dapr-identities
    export AZURE_STATICWEBSITE_NAME==daprdocs-v1-1
    

其中 daprdocs-v1-1 应更新为新的预览版本。

  1. 创建一个新的 azd 环境

    azd env new
    
  2. 出现提示时,输入一个新的环境名称。在此示例中,您可以将环境命名为:dapr-docs-v1-1

  3. 创建环境后,使用以下命令将 Dapr 文档 SWA 部署到新环境中:

    azd up
    
  4. 出现提示时,选择一个 Azure 订阅(Dapr Tests)和部署位置(West US 2)。

在 Azure 门户中配置 SWA

前往 Azure 门户 中的 Dapr 订阅,并验证您的新 Dapr 文档站点是否已部署。

可选地,使用门户中的 Static Web App > Access control (IAM) 边栏选项卡,为入站发布和出站访问依赖项授予正确的最小权限。

配置 DNS

  1. 在 Azure 门户中,从您刚创建的新 SWA 中,从左侧菜单导航到 Custom domains

  2. 复制 Web 应用的 “CNAME” 值。

  3. 使用您自己的账户,提交 CNCF 工单以创建一个映射到您复制的 CNAME 值的新域名。在此示例中,要为 Dapr v1.1 创建一个新域名,您需要请求映射到 v1-1.docs.dapr.io

    请求解析可能需要一些时间。

  4. 确认新域名后,返回门户中的静态 Web 应用。

  5. 导航到 Custom domains 边栏选项卡并选择 + Add

  6. 选择 Custom domain on other DNS

  7. Domain name 下输入 v1-1.docs.dapr.io。点击 Next

  8. Hostname record type 保留为 CNAME,并复制 Value 的值。

  9. 点击 Add

  10. 导航到 https://v1-1.docs.dapr.io 并验证空白网站是否正确加载。

您可以对任何预览版本重复这些步骤。

在新的 Dapr 发布日期

  1. 等待所有代码/容器/Helm chart 发布完成。
  2. 将从 release_v1.0v1.0 的 PR 合并。删除 release/v1.0 分支。
  3. 将从 release_v1.1v1.1 的 PR 合并。删除 release/v1.1 分支。
  4. 将从 release_v1.2v1.2 的 PR 合并。删除 release/v1.2 分支。

恭喜新的文档发布!🚀 🎉 🎈

拉取 SDK 文档更新

SDK 文档位于各个 SDK 仓库中。对 SDK 文档所做的更改会被推送到相关的 SDK 仓库。例如,要更新 Go SDK 文档,您需要将更改推送到 dapr/go-sdk 仓库。在您将最新的 dapr/go-sdk 提交拉取到 dapr/docs 当前版本分支之前,您的 Go SDK 文档更新不会反映在 Dapr 文档站点上。

要将 SDK 文档更新实时同步到 Dapr 文档站点,您需要执行一个简单的 git pull。此示例指的是 Go SDK,但适用于所有 SDK。

  1. 将最新的上游拉取到本地 dapr/docs 版本分支中。

  2. 更改到 dapr/docs 目录的根目录。

  3. 更改到 Go SDK 仓库。此命令将您带出 dapr/docs 上下文并进入 dapr/go-sdk 上下文。

    cd sdkdocs/go
    
  4. 切换到 dapr/go-sdk 中的 main 分支。

    git checkout main
    
  5. 拉取最新的 Go SDK 提交。

    git pull upstream main
    
  6. 更改到 dapr/docs 上下文以提交、推送和创建 PR。

后续步骤

7.3 - 建议的 Dapr 文档模板

新 Dapr 文档文章的建议模板指南

7.3.1 - 概念文章模板

创建概念文章的建议模板和指导

贡献新的概念或概述文章

概念(或概述)文章回答以下问题:

  • 为什么要关注此功能?
  • 它能帮助你解决哪些问题?

虽然组件、API 或 SDK 规范可能帮助读者了解如何使用或使用这些功能,但概念文章提供了更深层次和上下文。可以链接到规范文章,但尽量不要简单重复规范内容。

在命名概念文章时,确保名称、参数和术语与规范保持一致。必要时确保两者都得到更新。

了解更多关于贡献 Dapr 文档的信息,例如 front-mattershortcodes

模板

---
type: #Required; docs
title: #Required; 简短、清晰的标题
linkTitle: #Required; 简短标题
weight: #Required; 根据层级使用正确的权重
description: #Required; 一句话描述文章内容
---

<!--
在打开 PR 之前,请删除此模板中的所有注释。
-->

<!-- 
H1:Hugo front-matter 中的标题作为文章的 markdown H1。 
-->

<!-- 简介
必需。简要介绍文章将涵盖的概念。链接到相应的参考、规范或操作指南以提供上下文。 -->

<!-- 
如果可能,包含图表或图像。 
-->

## <第 1 节 H2>

<!-- 
在此添加你的内容。  
-->

## <第 2 节 H2>

<!-- 
每个 H2 步骤应以名词/描述性词开头。
-->

## <第 3 节 H2>

<!--
在此添加你的内容。
-->

<!--
在适用的地方,通篇包含图表或图像。
-->

## 尝试 <概念>

<!-- 
如果适用,包含一个部分,链接到相关的快速入门、操作指南或教程。 --> 

### 快速入门和教程

想要测试 Dapr <主题> API?完成以下快速入门和教程,查看 <主题> 的实际应用:

| 快速入门/教程 | 描述 |
| ------------------- | ----------- |
| [<主题> 快速入门](link) | 快速入门的描述。 |
| [<主题> 教程](link) | 教程的描述。 |

### 直接在应用中使用 <主题>

想要跳过快速入门?没问题。你可以直接在应用中尝试 <主题> 构建块。[安装 Dapr](link) 后,即可开始使用 <主题> API,从 [<主题> 操作指南](link) 开始。


-->

## 后续步骤

<!--
链接到相关页面和示例。例如,相关 API 规范、相关构建块等。
-->

7.3.2 - 快速入门指南模板

创建快速入门指南的建议模板和指导

贡献新的快速入门指南

Dapr 快速入门指南包含简明的说明,引导读者完成准备好的快速入门,这些内容保存在 dapr/quickstarts 仓库中。这些快速入门将整个功能或构建块打包在一起,使读者能够轻松体验其工作方式,而无需影响自己的项目。

快速入门说明应简洁、直接、清晰。快速入门指南的唯一目的是简单地指导读者完成准备好的快速入门。如果您想解释快速入门背后的概念,请引导读者阅读概念文章以获取更多背景信息。

了解有关贡献 Dapr 文档的更多信息,例如 front-mattershortcodes

模板

---
type: #必需;docs
title: #必需;"Quickstart: 简洁、清晰的标题"
linkTitle: #必需;这将显示在文档目录中
weight: #必需;根据层级使用正确的权重
description: #必需;一句话描述文章内容
---

<!--
在提交 PR 之前,请删除此模板中的所有注释。
-->

<!-- 
H1:Hugo front-matter 中的标题作为文章的 markdown H1。 
-->

<!-- 介绍性段落  
必需。简短的介绍,简要描述快速入门将涵盖的内容。链接到相应的概念或概述文档以提供背景信息。 -->

<!-- 
如果可能,请包含图表或图像。 
-->

<!-- 
确保快速入门包含多种编程语言的示例。 
-->

## 前置条件

<!--
通过列出读者可能需要的内容,确保读者为成功完成快速入门做好准备。
-->

## 步骤 1:设置环境

<!-- 
提供快速入门示例的链接,供读者克隆。 
-->

## 步骤 2:<动作或任务>

<!-- 
每个 H2 步骤应以动词/动作词开头。
-->

<!--
尽可能包含代码片段。 
-->

## 告诉我们您的想法!

我们正在不断努力改进我们的快速入门示例,并重视您的反馈。您觉得这个快速入门有帮助吗?您有改进建议吗?

加入我们的 [discord 频道](https://discord.gg/22ZtJrNe)参与讨论。

<!-- 由于 Dapr 是一个开放的贡献者社区,请确保提供 discord 讨论的链接以欢迎反馈。
-->

## 后续步骤

<!--
链接到相关页面和示例。例如,构建块概述、SDK 快速入门示例的 HTTP 版本等。
-->

<!--
使用按钮 shortcode 引导读者访问更深入的相关场景,例如 Dapr 教程。
-->

7.3.3 - How-to 指南模板

创建 how-to 指南的建议模板和指导

贡献新的 how-to 指南

How-to 指南为以下读者提供循序渐进的实践指导:

  • 启用某个功能
  • 集成某项技术
  • 在特定场景中使用 Dapr

与快速入门相比,how-to 指南可以被视为"进阶版"的自引导文档。How-to 场景所需时间更长,且更容易应用到读者的个人项目或环境中。

在命名 how-to 文档时,请在文件名中包含子目录名称。如果需要创建新的子目录,请确保其具有描述性,并包含相关组件或概念名称。例如,pubsub-namespaces

了解更多关于为 Dapr 文档做贡献的信息,例如 front-matter短代码

模板

---
type: #Required; docs
title: #Required; "How to: Brief, clear title"
linkTitle: #Required; "How to: Shorter than regular title, to show in table of contents"
weight: #Required; Use the correct weight based on hierarchy
description: #Required; One-sentence description of what to expect in the article
---

<!--
在提交 PR 之前移除此模板中的所有注释。
-->

<!-- 
H1:Hugo front-matter 中的标题作为文章的 markdown H1。 
-->

<!-- 导言段落  
必填。简短的介绍,简要描述 how-to 将涵盖的内容以及任何默认的 Dapr 特性。链接到相应的概念或概述文档以提供背景。-->

<!-- 
如果可能,包含图表或图像。 
-->

<!--
如果适用,在短代码注释或提示中链接到相关的快速入门,例如:

 如果尚未尝试,请先[试用 <主题> 快速入门](link),以快速了解如何使用 <主题>。

-->

<!-- 
确保 how-to 包含多种编程语言、操作系统或部署目标的示例(如果适用)。 
-->

## <操作或任务>

<!-- 
与快速入门不同,不要使用"步骤 1"、"步骤 2"等。  
-->

## <操作或任务>

<!-- 
每个 H2 步骤应以动词/动作词开头。
-->

-->
尽可能包含代码片段。 
-->

## 下一步

<!--
链接到相关页面和示例。例如,构建块概述、相关教程、API 参考等。
-->

8 - 为 Dapr Agents 做贡献

为 Dapr Agents 做贡献的指南

在为 Dapr Agents 做贡献时,应遵循以下规则和最佳实践。

示例

examples 目录包含代码示例,供用户运行以尝试各种 Dapr Agents 包和扩展的特定功能。在编写新的和更新的示例时,请记住:

  • 所有示例都应能在 Windows、Linux 和 MacOS 上运行。虽然 Python 代码在操作系统之间保持一致,但任何示例前/后命令应通过 codetabs 提供选项
  • 包含下载/安装任何必需先决条件的步骤。刚完成操作系统安装的用户应该能够开始示例并无误完成。链接到外部下载页面是可以的。

依赖项

此项目使用带有 pyproject.toml 的现代 Python 打包。依赖项管理如下:

  • 主要依赖项在 [project.dependencies]
  • 测试依赖项在 [project.optional-dependencies.test]
  • 开发依赖项在 [project.optional-dependencies.dev]

生成 Requirements 文件

如果需要生成 requirements 文件(例如,用于部署或特定环境):

# Generate requirements.txt
pip-compile pyproject.toml

# Generate dev-requirements.txt
pip-compile pyproject.toml --extra dev

安装依赖项

# Install main package with test dependencies
pip install -e ".[test]"

# Install main package with development dependencies
pip install -e ".[dev]"

# Install main package with all optional dependencies
pip install -e ".[test,dev]"

测试

项目使用 pytest 进行测试。要运行测试:

# Run all tests
tox -e pytest

# Run specific test file
tox -e pytest tests/test_random_orchestrator.py

# Run tests with coverage
tox -e pytest --cov=dapr_agents

代码质量

项目使用多种工具来维护代码质量:

# Run linting
tox -e flake8

# Run code formatting
tox -e ruff

# Run type checking
tox -e type

开发工作流

  1. 安装开发依赖项:

    pip install -e ".[dev]"
    
  2. 在进行更改之前运行测试:

    tox -e pytest
    
  3. 进行更改

  4. 运行代码质量检查:

    tox -e flake8
    tox -e ruff
    tox -e type
    
  5. 再次运行测试:

    tox -e pytest
    
  6. 提交更改

GitHub Dapr Bot 命令

查看 daprbot 文档 以了解您可以在此仓库中运行的用于常见任务的 GitHub 命令。例如,您可以运行 /assign(作为 issue 上的评论)将 issue 分配给用户或用户组。

反馈

此页面是否有帮助?

9 - 协议参考

Dapr 运行时协议和每个构建块内部机制的底层技术文档。

本节深入探讨 Dapr 运行时的内部工作原理。面向维护者、贡献者以及对 Dapr 构建块底层实现细节感兴趣的任何人。

与面向用户的 API 参考不同,这些文档侧重于:

  • 运行时如何处理请求。
  • 内部状态转换。
  • 与组件接口的交互。
  • 协议层面的细节(gRPC 和 HTTP)。

构建块

选择一个构建块以探索其内部协议和机制:

9.1 - 工作流协议

工作流构建块底层机制的详细描述。

本文档从底层详细说明 Dapr 工作流协议与运行时约定。面向的读者是构建工作流 Worker 的 SDK 作者以及演进 Dapr 边车工作流引擎的运行时维护者。

概述

Dapr 工作流采用"边车即调度器"模式:Dapr 运行时(边车)作为工作流引擎,应用 SDK 作为工作流 Worker。所有控制与执行流量均通过 gRPC 传输。

协议界面分为两类:

  1. 管理 API(通过 SDK 可访问的标准 Dapr gRPC):
    • 启动、终止、暂停、恢复、重新运行、清除以及查询工作流实例。
  2. 执行 API(Task Hub 协议):
    • Worker 侧接口,用于接收编排/活动工作项并上报完成(例如通过 TaskHubSidecarService)。

核心组件

  • 工作流引擎(Dapr 边车)

    管理工作流状态转换、历史持久化、编排与活动任务的调度,以及可靠交付语义。默认情况下,利用 Dapr Actors 作为后端,实现可持久化、分区的执行。

  • 工作流 Worker(应用 SDK)

    连接到边车,轮询编排与活动工作项,执行用户定义的逻辑,并将结果、失败与心跳返回给引擎。编排逻辑必须是确定性的;活动逻辑无需是确定性的。

  • 编排

    定义工作流的确定性协调器。引擎通过重放历史来驱动编排,重建状态并调度出站任务(活动、子编排、定时器、外部事件)。

  • 活动

    原子工作单元。活动执行保证至少一次,并将结果或失败报告回引擎。建议幂等,并在上下文中提供任务执行标识符以辅助实现幂等。

  • 状态存储与后端

    工作流历史与状态持久化保存。引擎通常在所选持久化层上实现任务中心模式,并使用 Dapr Actors 作为默认的可靠性底层。

执行模型

Dapr 工作流基于持久任务框架(DTFx)执行语义:

基于重放的执行

编排器从其事件历史重放,以重建确定性状态。所有非确定性操作(时间、随机值、I/O)必须由引擎中介(例如通过定时器、活动调用、外部事件)。

确定性编排器

除引擎中介效果外,编排器代码必须无副作用。控制流在重放期间必须可复现。

活动至少一次交付、状态提交恰好一次

活动可能被多次传递。引擎确保工作流状态提交是幂等的,并恰好一次应用。

边车即调度器

边车拥有调度权,并在向 Worker 分发工作前持久化所有历史/事件。从引擎视角看,Worker 是无状态执行器。

协议界面

  1. 管理 API(标准 Dapr gRPC)
  • 启动工作流:创建并持久化初始历史事件;返回实例元数据。
  • 终止 / 暂停 / 恢复:通过持久化的控制事件驱动生命周期转换。
  • 查询:检索实例状态、历史、输出、失败详情及自定义元数据。
  • 重新运行:从历史事件启动新的工作流实例。
  • 清除:主动清除工作流历史与状态。

注意:关于确切的 RPC 形状、错误码和语义,参见 管理 API 规范

  1. 执行 API(Task Hub 协议)
  • 轮询工作:Worker 获取编排与活动工作项。
  • 完成 / 失败工作:Worker 报告完成结果或失败;引擎将这些追加到历史并推进编排进度。
  • 心跳 / 租约:用于长时间运行的活动与协作式重新平衡的可选机制。
  • 定时器与外部事件:作为历史事件传递给编排,以保持重放的确定性。

注意:参见定义了 TaskHubSidecarService 约定、负载架构与排序规则的 执行 API 规范

请求与运行时生命周期

  1. 启动工作流
  • 客户端通过管理 API 调用 StartWorkflow
  • 引擎持久化初始事件(例如 ExecutionStarted)并实例化实例。
  1. 编排器执行(重放驱动)
  • 引擎重放编排历史以重建状态。
  • 编排器通过发布命令调度效果(活动、子编排、定时器),引擎将其持久化为新的历史事件。
  1. 活动分发与执行
  • 引擎将活动工作项分发给 Worker。
  • Worker 运行活动(可能重试,并至少一次交付)。
  • Worker 以完成(结果)或失败响应;引擎追加到历史。
  1. 定时器与外部信号
  • 引擎将定时器触发或外部事件记录作为历史条目交付。
  • 编排器在下次重放时确定性地消费这些事件。
  1. 推进与检查点
  • 每一步追加到历史日志并推进编排状态。
  • 引擎保证编排状态的幂等性与恰好一次提交。
  1. 完成
  • 编排返回输出(成功)或失败(异常详情)。
  • 最终状态与输出被持久化;状态查询反映终止状态。

协议原则

  • GRIEF(GRpc IntErFace):所有 Worker/引擎与客户端/引擎通信均为 gRPC。
  • 基于重放的编排:通过历史重放保证确定性。
  • 活动至少一次交付:活动可能重新执行;应设计为幂等。
  • 引擎中介的效果:所有非确定性/时间/IO 均通过引擎流动以保持重放安全。

文档目录

  1. 管理 API 详细的 Dapr gRPC 控制平面操作与负载。
  2. 执行 API(Task Hub 协议) TaskHubSidecarService Worker 协议、工作项约定、结果/失败报告与排序。
  3. 编排生命周期 重放语义、调度、外部事件、定时器与完成。
  4. 活动生命周期 分发、重试、幂等、心跳语义与失败处理。
  5. 状态与历史 历史架构、状态快照与持久化保证。
  6. 版本管理 Dapr 如何处理同一工作流定义的多个版本。

9.1.1 - Workflow 协议 - 管理 API

Workflow 构建块内部的底层描述。

Workflow 管理 API

Workflow 管理 API 允许 Dapr 客户端控制 workflow 实例的生命周期。这些 API 通过标准 Dapr gRPC 端点公开,通常通过 SDK 提供。

gRPC 服务定义

管理 API 是 dapr.proto.runtime.v1Dapr 服务的一部分。虽然可能存在多个版本(Alpha1、Beta1),以下描述的是当前的实现逻辑。

StartWorkflow

启动一个新的 workflow 实例。

请求(StartWorkflowRequest):

字段类型描述
instance_idstring可选。workflow 实例的唯一标识符。如果未提供,Dapr 将生成一个随机的 UUID。
workflow_componentstring要使用的 workflow 组件的名称。目前,Dapr 使用内置引擎。
workflow_namestring要执行的 workflow 定义的名称。
optionsmap<string, string>可选。组件特定选项。
inputbytes可选。workflow 实例的输入数据,通常是 JSON 序列化字符串。

响应(StartWorkflowResponse):

字段类型描述
instance_idstring已启动的 workflow 实例的 ID。

GetWorkflow

检索 workflow 实例的当前状态和元数据。

请求(GetWorkflowRequest):

字段类型描述
instance_idstring要查询的 workflow 实例的 ID。
workflow_componentstringworkflow 组件的名称。

响应(GetWorkflowResponse):

字段类型描述
instance_idstringworkflow 实例的 ID。
workflow_namestringworkflow 的名称。
created_atTimestamp实例创建的时间。
last_updated_atTimestamp实例最后更新的时间。
runtime_statusstring状态(例如 RUNNINGCOMPLETEDFAILEDTERMINATEDPENDING)。
propertiesmap<string, string>额外的组件特定元数据。

TerminateWorkflow

强制终止正在运行的 workflow 实例。

请求(TerminateWorkflowRequest):

字段类型描述
instance_idstring要终止的 workflow 实例的 ID。
workflow_componentstringworkflow 组件的名称。

RaiseEventWorkflow

向正在运行的 workflow 实例发送事件。

请求(RaiseEventWorkflowRequest):

字段类型描述
instance_idstringworkflow 实例的 ID。
workflow_componentstringworkflow 组件的名称。
event_namestring要引发的事件的名称。
event_databytes与事件关联的数据。

PauseWorkflow & ResumeWorkflow

暂停或恢复 workflow 实例。

请求(PauseWorkflowRequest / ResumeWorkflowRequest):

字段类型描述
instance_idstringworkflow 实例的 ID。
workflow_componentstringworkflow 组件的名称。

PurgeWorkflow

移除与 workflow 实例关联的所有状态和历史记录。这通常只能对已完成、失败或已终止的实例执行。

请求(PurgeWorkflowRequest):

字段类型描述
instance_idstringworkflow 实例的 ID。
workflow_componentstringworkflow 组件的名称。

ListInstanceIDs(仅限 Task Hub 协议)

检索 workflow 实例 ID 列表,可选择按状态或名称筛选。这目前是内部 Task Hub 协议的一部分,用于管理工具中的分页。

请求(ListInstanceIDsRequest):

字段类型描述
page_sizeint32要返回的 ID 的最大数量。
continuation_tokenstring用于检索下一页结果的不透明 token。

响应(ListInstanceIDsResponse):

字段类型描述
instance_idsrepeated string实例 ID 列表。
continuation_tokenstring用于下一页结果的 token。

继续标记和分页

continuation_token 是由 Dapr 运行时(以及底层状态存储)生成的不透明字符串。其目的是允许客户端可靠地对大量 workflow 实例进行分页,而无需一次性将所有 ID 加载到内存中。

SDK 对继续标记的要求:

  1. 不透明性:SDK 必须将此 token 视为黑盒。它不应尝试解析、修改或构造自己的 token。
  2. 状态管理:当 SDK 收到 ListInstanceIDsResponse 时,如果打算获取更多结果,应存储 continuation_token
  3. 请求传播:要获取下一页,SDK 必须将从上一个响应中收到的确切 continuation_token 传递到下一个 ListInstanceIDsRequest
  4. 终止:响应中的空或 null continuation_token 表示没有更多页面可检索。

运行时行为: 运行时从状态存储的 KeysLike 操作派生此 token。由于它与底层数据库的分页机制相关联,因此该 token 可能具有过期时间或与初始请求中使用的特定查询参数(如 page_size)相关联。

实现细节

边车接收这些请求并将其转换为底层 durabletask-go 客户端的操作。例如,StartWorkflow 调用后端以创建新的业务流程实例并排队 ExecutionStarted 事件。

9.1.2 - Workflow Protocol - Execution API

Workflow 构建块内部机制的低级描述。

Workflow Execution API (Task Hub Protocol)

Workflow Execution API 是一个低级 gRPC 协议,Dapr Workflow SDK 通过它充当"Worker"。SDK 通过此协议连接 Dapr sidecar 以轮询任务并报告完成状态。

该服务名为 TaskHubSidecarService

Service Definition (gRPC)

service TaskHubSidecarService {
  rpc GetWorkItems(GetWorkItemsRequest) returns (stream WorkItem);
  rpc CompleteOrchestratorTask(OrchestratorResponse) returns (CompleteBatchResponse);
  rpc CompleteActivityTask(ActivityResponse) returns (CompleteBatchResponse);
  // ... other management methods
}

Worker 生命周期

  1. 连接:SDK 打开一个到 GetWorkItems 的长连接双向流。
  2. 轮询:SDK 从流中接收 WorkItem 消息。
  3. 执行
    • 如果工作项是 Orchestration,SDK 获取并重放历史事件以确定下一步操作。
    • 如果工作项是 Activity,SDK 执行活动逻辑。
  4. 完成
    • 对于 Orchestration,SDK 调用 CompleteOrchestratorTask 并附带要执行的操作列表。
    • 对于 Activity,SDK 调用 CompleteActivityTask 并附带结果或失败信息。

gRPC Service: TaskHubSidecarService

GetWorkItems

打开流以接收 orchestration 和 activity 的工作项。

Request (GetWorkItemsRequest): 通常为空或包含 worker 元数据。

Response (stream WorkItem): WorkItem 可以是以下之一:

  • orchestrator_item:包含某个 orchestration 的历史和新事件。
  • activity_item:包含单个 activity 任务的详情。

CompleteOrchestratorTask

报告 orchestration 执行的结果。

Request (OrchestratorResponse):

  • instance_id:workflow 实例的 ID。
  • actionsOrchestratorAction 消息列表。
  • custom_status:可选的用户定义状态字符串。

OrchestratorAction 类型:

  • ScheduleTask:调度一个新的 activity。
  • CreateTimer:调度一个持久化计时器。
  • CreateSubOrchestration:启动一个子 workflow。
  • CompleteOrchestration:将 workflow 标记为完成(成功或失败)。
  • TerminateOrchestration:强制终止实例。
  • SendEvent:向另一个 workflow 发送事件。

CompleteActivityTask

报告 activity 执行的结果。

Request (ActivityResponse):

  • instance_id:workflow 实例的 ID。
  • task_id:activity 任务的唯一 ID。
  • completion_tokenActivityWorkItem 中收到的 opaque token。
  • result:activity 的序列化输出(如果成功)。
  • failure_details:错误详情(如果失败)。

数据模型

HistoryEvent

Dapr 中的 workflow 是事件溯源的。Orchestration 的状态通过重放一系列 HistoryEvent 消息来重建。

常见事件类型:

  • ExecutionStarted:初始事件,包含 workflow 名称和输入。
  • TaskScheduled:一个 activity 被调度。
  • TaskCompleted:一个 activity 成功完成。
  • TaskFailed:一个 activity 失败。
  • TimerCreated:一个计时器被调度。
  • TimerFired:一个计时器到期。
  • OrchestrationCompleted:workflow 完成。

FailureDetails

用于报告来自 activity 或 orchestration 的错误。

  • error_type:标识错误类型的字符串。
  • error_message:人类可读的错误消息。
  • stack_trace:可选的堆栈跟踪。
  • is_non_retriable:布尔标志。

协议细节

  • 流式传输GetWorkItems 是服务端到客户端的流。Dapr 在工作可用时将工作推送到 SDK。
  • 粘性会话:Dapr 尝试将同一实例的工作项发送到同一 worker(如果可能),但 SDK 不能依赖此特性来保证正确性。
  • 确定性:SDK 必须确保 orchestration 逻辑是确定性的。在重放期间,SDK 使用 OrchestratorWorkItem 中提供的历史记录,以避免重新执行已记录的操作。

9.1.3 - Workflow Protocol - Orchestration Lifecycle

Workflow 构建块内部机制的底层描述。

编排生命周期

本文档从协议层面描述编排的生命周期,特别是 Dapr 引擎与 SDK 如何交互以可靠地执行工作流逻辑。

基于重放的执行

Dapr Workflow 使用 事件溯源重放 来维护状态。Dapr 不是保存整个 worker 进程的状态(栈、变量等),而是保存已发生事件的历史记录。

重放循环

  1. 工作项到达:Dapr 引擎通过 GetWorkItems 流向 SDK 发送一个 OrchestratorWorkItem。该工作项包含工作流实例的完整历史记录以及任何新事件(例如,activity 完成或外部事件)。
  2. 重建:SDK 从头开始执行编排函数。
  3. 确定性执行:随着函数的执行,它会遇到"任务"(例如,调用 activity、休眠)。
    • 对于每个任务,SDK 会检查提供的 History 以查看该任务是否已完成。
    • 如果任务在历史记录中,SDK 会立即返回记录的结果,而无需实际重新执行任务逻辑。
    • 如果任务不在历史记录中,SDK 会记录该任务需要被调度并暂停编排函数的执行(通常通过抛出特殊异常或返回待处理的 promise)。
  4. 报告:一旦编排函数被暂停或完成,SDK 会向 Dapr 发送 CompleteOrchestratorTask 请求。该请求包含引擎应执行的一系列 Actions(例如 ScheduleTaskCreateTimer)。
  5. 状态提交:Dapr 引擎接收这些操作,在状态存储中更新工作流历史记录,并调度任何请求的任务(例如,通过向 activity worker 发送工作)。

分步示例

假设一个工作流:Activity A -> Activity B

1. 工作流启动

  • 引擎:将 ExecutionStarted 事件加入队列。
  • SDK:接收包含 [ExecutionStarted]OrchestratorWorkItem
  • SDK:运行函数。函数调用 Activity A
  • SDK:检查历史记录。Activity A 不在其中。
  • SDK:暂停。发送包含 [ScheduleTask(Activity A)]CompleteOrchestratorTask
  • 引擎:在历史记录中记录 TaskScheduled(Activity A)

2. Activity A 完成

  • 引擎:在历史记录中记录 TaskCompleted(Activity A, result="foo")
  • SDK:接收包含 [ExecutionStarted, TaskScheduled(A), TaskCompleted(A)]OrchestratorWorkItem
  • SDK:从头开始运行函数。
  • SDK:函数调用 Activity A。SDK 在历史记录中找到 TaskCompleted(A)。返回 "foo"
  • SDK:函数调用 Activity B
  • SDK:检查历史记录。Activity B 不在其中。
  • SDK:暂停。发送包含 [ScheduleTask(Activity B)]CompleteOrchestratorTask
  • 引擎:记录 TaskScheduled(Activity B)

3. 工作流完成

  • Activity B 完成。
  • SDK:接收包含 A 和 B 都已完成的历史记录。
  • SDK:运行函数。A 和 B 都从历史记录返回结果。
  • SDK:函数完成并返回最终结果。
  • SDK:发送包含 [CompleteOrchestration(result="final")]CompleteOrchestratorTask
  • 引擎:记录 OrchestrationCompleted 并将实例标记为 COMPLETED

SDK 作者的关键要求

1. 确定性

编排函数必须是确定性的。它不能使用:

  • 随机数。
  • 当前日期/时间(必须使用持久化计时器或提供的 CurrentUtcDateTime)。
  • 直接 IO(必须在 activity 中完成)。
  • 在重放之间可能更改的全局状态。

2. 修补(运行中更新)

当工作流已经在运行时,您可能需要更新其逻辑。然而,由于工作流是基于重放的,直接更改逻辑会破坏进行中实例的确定性。

Dapr 提供了 Patching 机制(例如 ctx.IsPatched("patch-id"))来安全地引入更改:

  • 逻辑分支:SDK 提供一个 API 来检查特定"补丁"是否对当前实例处于活动状态。
  • 补丁记录:当执行过程中遇到补丁检查时,结果(true/false)会被记录在工作流历史中。
  • 一致性:一旦补丁被记录为对实例活动(或非活动),它将在该实例的生命周期内保持如此,即使 worker 代码更改或实例被移动到另一个 worker。
  • 安全性:Dapr 引擎验证重放期间遇到的补丁序列是否与历史中的序列完全匹配。如果不匹配,工作流将进入 Stalled 状态以防止数据损坏。

3. 命名版本(运行中更新)

Dapr 还提供了 命名版本控制 机制,其中 SDK 维护可用命名工作流版本的注册表。当它收到通过名称初始化新工作流的请求时,它将查询注册表以确定该名称是否匹配与指定工作流名称不同的工作流版本,并负责将请求重定向到预期的"最新"版本。

  • 逻辑分支:SDK 提供一个 API 来为给定的工作流名称注册不同的版本。
  • 重放一致性:运行工作流的请求可能包含一个属性,指定要执行的特定工作流名称。这确保进行中的工作流将始终使用相同的工作流版本运行,而新工作流将使用最新的可用版本。

3. 停滞状态

当引擎检测到需要手动干预或代码修复才能继续的不可恢复条件时,工作流实例进入 STALLED 状态。常见原因包括:

  • 补丁不匹配:当前代码的补丁逻辑与实例的历史记录相矛盾。
  • 执行错误:发生了无法通过重试处理的致命错误。

当停滞时,实例停止执行但保留在系统中。一旦根本问题得到解决(例如,部署了正确的代码版本),实例就可以恢复或将在下一个事件时自动恢复。

4. 历史管理

SDK 必须高效地搜索历史记录。通常,这是通过维护执行期间遇到的任务计数器并将它们与历史中的事件序列进行匹配来完成的。

5. 优雅暂停

SDK 需要一种机制,在任务已调度但尚未完成时停止编排函数的执行,同时不丢失稍后重新启动它的能力。

9.1.4 - Workflow Protocol - Activity Lifecycle

Workflow 构建块内部机制的低层级描述。

Activity 生命周期

Activity 是 Dapr Workflow 中的基本工作单元。与编排不同,Activity 不会重放,也不需要具有确定性。它们每次"调度"仅执行一次(尽管可能会发生重试)。

执行流程

  1. 调度:编排通过向 Dapr 引擎发送 ScheduleTask 操作来请求一个 Activity。
  2. 工作项分发:Dapr 引擎将 Activity 任务加入队列。当 Activity worker(SDK)可用时,引擎通过 GetWorkItems 流发送一个 ActivityWorkItem
  3. 执行:SDK 接收 ActivityWorkItem,其中包含:
    • name:要执行的 Activity 的名称。
    • input:Activity 的输入数据。
    • instance_id:调度该 Activity 的工作流实例的 ID。
    • task_id:此特定 Activity 执行的唯一标识符。
    • task_execution_id:此特定 Activity 的特定尝试的唯一标识符。这对于在 Activity 逻辑中实现幂等性很有用。
    • completion_token:一个不透明 token,用于将响应与此特定工作项关联。
  4. 报告:Activity 逻辑完成后,SDK 向 Dapr 发送 CompleteActivityTask 请求。
    • 成功:SDK 在 result 字段中提供序列化输出。
    • 失败:SDK 提供 failure_details(错误消息、类型、堆栈跟踪)。

Task Execution ID

task_execution_id(也称为 Task Execution Key)是一个唯一的、运行时生成的字符串(通常是 UUID),用于标识执行 Activity 任务的特定尝试

对 SDK 的重要性

虽然 Workflow SDK 在工作项之间通常是无状态的,但 task_execution_idActivity Worker 提供了关键的上下文:

  1. 分布式幂等性:如果 Activity 执行副作用(例如,扣款信用卡),它应该使用 task_execution_id 作为幂等性键。
  2. 区分重试:与 task_id(在工作流中特定步骤保持不变)不同,task_execution_id 在引擎每次重试 Activity 时都会变化(例如,由于超时或 worker 崩溃)。
  3. Zombie 检测:如果 Activity worker 花费时间过长,引擎使其超时并在另一个 worker 上重试,原始 worker 可能最终会完成。通过与持久存储或外部 API 核对 task_execution_id,worker 可以确定它是否是一个不再需要其结果的"zombie"。

SDK 实现指南:

  • 向用户公开:SDK 必须将 task_execution_id 公开给 Activity 实现逻辑(例如,通过 ActivityContext)。
  • 不要缓存:SDK 不应尝试跨不同工作项缓存或重用此 ID。
  • 不透明使用:SDK 应将该值视为不透明字符串。它由 Dapr 边车在分发 Activity 时生成,不是 SDK 需要创建或解析的东西。

Completion Tokens

completion_token 是由 Dapr 运行时生成的不透明字符串,并作为 ActivityWorkItem 的一部分传递给 SDK。

目的和意图

  1. 响应关联:边车使用 completion_tokenActivityResponse(来自 CompleteActivityTask)可靠地匹配到它分发的原始任务。
  2. 无状态跟踪:它允许边车在接收完成时保持无状态或最小化状态查找,因为 token 包含(或指向)必要的上下文(实例 ID、任务 ID 等)。
  3. Zombie 防止:如果 Activity 超时并被重试,新尝试将具有不同的 completion_token。如果原始"zombie" worker 最终使用旧 token 响应,边车可以轻松识别并忽略延迟的响应。

SDK 实现指南

  • 捕获:SDK 必须从传入的 ActivityWorkItem 中捕获 completion_token
  • 传播:SDK 必须在通过 CompleteActivityTask 发送的 ActivityResponse 中包含完全相同的 completion_token
  • 不透明性:SDK 必须将 token 视为黑盒。它不应尝试解析、修改或构造自己的 token。
  • 存储:在 Activity 执行期间,SDK 必须将此 token 保存在内存中(例如,在 ActivityContext 中)。

Task Activity IDs

在 Dapr 运行时中(特别是使用 Actors 后端时),Activity 被表示为 actors。每个 Activity 执行都有一个唯一的 Task Activity ID(也称为 Activity Actor ID)。

ID 遵循特定模式: {workflowInstanceID}::{taskID}::{generation}

  • workflowInstanceID:调度该 Activity 的工作流实例的唯一 ID。
  • taskID:工作流执行中任务的序列号(例如,0、1、2…)。
  • generation:如果工作流被重启或"继续作为新工作流",该计数器会递增。

这个唯一的 ID 确保 Activity 执行被隔离,并且可以在重试和重启期间可靠地跟踪。

重试

Dapr 根据编排中定义的策略处理 Activity 重试(如果 SDK 支持在 ScheduleTask 操作中定义重试策略)。如果 Activity 失败并且存在重试策略,引擎将在指定的延迟后重新将 Activity 任务加入队列。

从 Activity worker 的角度来看,重试只是一个具有相同名称和输入的新 ActivityWorkItem,但可能具有不同的 task_id(或相同的,取决于后端实现)。

幂等性

因为 Activity 可能被执行多次(例如,如果 worker 在执行之后但在报告完成之前崩溃),所以建议 Activity 逻辑尽可能具有幂等性。

与工作流的比较

功能编排Activity
执行方式基于重放(确定性)直接执行
状态通过历史事件管理无内部工作流状态
副作用禁止(必须使用 Activity)允许(IO、数据库等)
生命周期可以长时间运行(天/月)通常短期
连接性通过 GetWorkItems 连接通过 GetWorkItems 连接

9.1.5 - Workflow Protocol - State & History

Workflow 构建块内部机制的低层描述。

状态和历史管理

Dapr Workflows 采用事件溯源模式,这意味着工作流的状态是从一系列事件中推导出来的。本文档描述了 Dapr 如何存储和管理这些历史记录和状态。

后端存储:Dapr Actors

默认情况下,Dapr Workflow 引擎使用 Dapr Actors 作为其存储后端。每个工作流实例都映射到一个唯一的 actor 实例。这提供了以下特性:

  • 并发控制:Actors 确保在任意时刻只有一个操作在处理工作流实例。
  • 可靠性:Actor 状态会持久化到配置好的 Dapr 状态存储中。
  • 定时器:Dapr Actors 提供持久化提醒,用于实现工作流定时器。

工作流状态架构

工作流实例(actor)的状态由以下几个组件构成:

1. 元数据

存储有关该实例的高级信息:

  • instance_id:工作流的唯一 ID。
  • name:工作流的名称。
  • status:当前运行时状态(如 Running、Completed、Stalled 等)。
  • version:工作流版本的名称及任何活跃的补丁。
  • created_at:创建时间戳。
  • last_updated_at:最后活动时间戳。
  • input:原始输入数据。
  • output:最终输出数据(如已完成的)。

2. 历史记录

一系列 HistoryEvent 对象,记录工作流中发生的所有事件。为了优化大型历史记录,Dapr 通常将历史事件以分块或单独键值的形式存储在状态存储中:

  • 键格式wf-history-<instance_id>-<index>
  • 事件内容:序列化的 protobuf 消息,包含事件类型、时间戳以及类型特定的数据(如 TaskScheduledTaskCompleted)。

3. 收件箱(待处理事件)

已发生但尚未被编排器处理(重放)的事件集合。包括:

  • 向工作流引发的外部事件。
  • 已完成的 activity 结果。
  • 已触发的定时器。

当编排器下次运行时,它会"排空"收件箱,将这些事件移入历史记录,然后重放逻辑。

重放与状态重建

当 Worker(SDK)收到工作项时,Dapr 提供历史事件。SDK 通过按顺序重放这些事件来重建编排的内部状态(例如本地变量、当前执行点)。

确定性原则与历史记录

历史记录是"真相的来源"。如果编排代码以非确定性的方式发生变化(例如在现有代码中间添加新的 activity 调用),重放将失败,因为代码的请求将与记录的历史记录不匹配。

清除状态

当清除工作流时,Dapr 会从状态存储中删除元数据及所有相关的历史事件键。这通常是为了在完成工作流后进行清理。

9.1.6 - 工作流协议 - 版本控制

工作流构建块内部实现的底层描述。

工作流版本控制

Dapr 工作流支持工作流定义的版本控制,允许你在现有实例继续运行其原始逻辑的同时更新工作流逻辑。

命名工作流版本控制

向 Dapr 引擎注册工作流时,你可以提供版本名称。这允许同一工作流的多个版本共存。

  • 默认版本:一个工作流版本可被标记为默认版本。如果客户端仅通过名称启动工作流而不指定版本,则使用默认版本。
  • 特定版本:客户端可以在启动新实例时请求工作流的特定版本。

注册 API

SDK 使用其任务注册表中的 AddVersionedOrchestrator(或类似)方法来注册版本化工作流。

// 示例(内部注册表 API)
registry.AddVersionedOrchestrator("MyWorkflow", "v2", true, MyWorkflowV2)
registry.AddVersionedOrchestrator("MyWorkflow", "v1", false, MyWorkflowV1)

边车版本控制

Dapr 边车会跟踪实例正在运行的工作流命名版本,以及在该工作流执行过程中已应用的补丁列表。此信息存储在工作流历史记录中,位于 OrchestratorStarted 事件的 version 字段内。

message OrchestrationVersion {
  string name = 1;
  repeated string patches = 2;
}

当实例恢复时(例如,在某个活动完成后),Dapr 引擎负责处理因客户端版本不匹配而导致的停滞。它通过监控 placement 表的变化来实现这一点,这表明有新的 SDK 客户端已连接(可能代表应用程序的较新副本实例),并调度重放最后一个事件以尝试重试该操作。这一次,如果 SDK 能够成功完成任务,运行时将把工作流状态从 Stalled 更改回 Running