业务流程协调程序函数代码约束Orchestrator function code constraints

Durable Functions 是 Azure Functions 的一个扩展,用于构建有状态应用。Durable Functions is an extension of Azure Functions that lets you build stateful apps. 可以使用业务流程协调程序函数来协调函数应用中其他持久函数的执行。You can use an orchestrator function to orchestrate the execution of other durable functions within a function app. 业务流程协调程序函数带有状态且可靠,可以长时间运行。Orchestrator functions are stateful, reliable, and potentially long-running.

业务流程协调程序代码约束Orchestrator code constraints

业务流程协调程序函数使用事件溯源来确保执行的可靠性,并保留本地变量状态。Orchestrator functions use event sourcing to ensure reliable execution and to maintain local variable state. 业务流程协调程序代码的重播行为针对可在业务流程协调程序函数中编写的代码类型创建约束。The replay behavior of orchestrator code creates constraints on the type of code that you can write in an orchestrator function. 例如,业务流程协调程序函数必须具有确定性:业务流程协调程序函数将重播多次,每次必须生成相同的结果。 For example, orchestrator functions must be deterministic: an orchestrator function will be replayed multiple times, and it must produce the same result each time.

使用确定性 APIUsing deterministic APIs

本部分提供一些简单的指导原则,用于帮助确保代码是确定性的。This section provides some simple guidelines that help ensure your code is deterministic.

业务流程协调程序函数可以采用目标语言调用任何 API。Orchestrator functions can call any API in their target languages. 但是,必须确保业务流程协调程序函数只调用确定性 API。However, it's important that orchestrator functions call only deterministic APIs. 确定性 API 是一种在输入相同时始终返回同一值的 API,不管何时调用,也不管调用频率如何。A deterministic API is an API that always returns the same value given the same input, no matter when or how often it's called.

下表显示了应该避免使用的 API 的一些示例,因为它们不是确定性的。 The following table shows examples of APIs that you should avoid because they are not deterministic. 这些限制仅适用于业务流程协调程序函数。These restrictions apply only to orchestrator functions. 其他函数类型没有此类限制。Other function types don't have such restrictions.

API 类别API category ReasonReason 解决方法Workaround
日期和时间Dates and times 返回当前日期或时间的 API 是非确定性的,因为每次重播时它们返回的值都不相同。APIs that return the current date or time are nondeterministic because the returned value is different for each replay. 使用 .NET 中的 CurrentUtcDateTime API 或 JavaScript 中的 currentUtcDateTime API,可以安全地进行重播。Use theCurrentUtcDateTime API in .NET or the currentUtcDateTime API in JavaScript, which are safe for replay.
GUID 和 UUIDGUIDs and UUIDs 返回随机 GUID 或 UUID 的 API 是非确定性的,因为每次重播时它们生成的值都不相同。APIs that return a random GUID or UUID are nondeterministic because the generated value is different for each replay. 使用 .NET 中的 NewGuid 或 JavaScript 中的 newGuid 安全地生成随机 GUID。Use NewGuid in .NET or newGuid in JavaScript to safely generate random GUIDs.
随机数Random numbers 返回随机数的 API 是非确定性的,因为每次重播时它们生成的值都不相同。APIs that return random numbers are nondeterministic because the generated value is different for each replay. 使用活动函数将随机数返回给业务流程。Use an activity function to return random numbers to an orchestration. 就重播来说,活动函数的返回值始终是安全的。The return values of activity functions are always safe for replay.
绑定Bindings 输入和输出绑定通常会执行 I/O 操作,是非确定性的。Input and output bindings typically do I/O and are nondeterministic. 即使是业务流程客户端实体客户端绑定,也不得由业务流程协调程序函数直接使用。An orchestrator function must not directly use even the orchestration client and entity client bindings. 在客户端或活动函数中使用输入和输出绑定。Use input and output bindings inside client or activity functions.
网络Network 网络调用涉及外部系统,是非确定性的。Network calls involve external systems and are nondeterministic. 使用活动函数进行网络调用。Use activity functions to make network calls. 如果需要从业务流程协调程序函数进行 HTTP 调用,则也可使用持久性 HTTP APIIf you need to make an HTTP call from your orchestrator function, you also can use the durable HTTP APIs.
阻止 APIBlocking APIs 阻止 .NET 中的 Thread.Sleep 等 API 和类似的 API 可能导致业务流程协调程序函数出现性能和缩放问题,应该避免使用。Blocking APIs like Thread.Sleep in .NET and similar APIs can cause performance and scale problems for orchestrator functions and should be avoided. 在 Azure Functions 消耗计划中,它们甚至可能导致不必要的运行时收费。In the Azure Functions Consumption plan, they can even result in unnecessary runtime charges. 在适用的情况下使用阻止 API 的替代方案。Use alternatives to blocking APIs when they're available. 例如,使用 CreateTimer 在业务流程执行中引入延迟。For example, use CreateTimer to introduce delays in orchestration execution. 持久计时器延迟不计入业务流程协调程序函数的执行时间。Durable timer delays don't count towards the execution time of an orchestrator function.
异步 APIAsync APIs 除非使用 IDurableOrchestrationContext API 或 context.df 对象的 API,否则业务流程协调程序代码不得启动任何异步操作。Orchestrator code must never start any async operation except by using the IDurableOrchestrationContext API or the context.df object's API. 例如,在 .NET 中不能使用 Task.RunTask.DelayHttpClient.SendAsync,在 JavaScript 中不能使用 setTimeoutsetIntervalFor example, you can't use Task.Run, Task.Delay, and HttpClient.SendAsync in .NET or setTimeout and setInterval in JavaScript. Durable Task Framework 在单个线程上执行业务流程协调程序代码。The Durable Task Framework runs orchestrator code on a single thread. 它不能与可由其他异步 API 调用的其他任何线程交互。It can't interact with any other threads that might be called by other async APIs. 业务流程协调程序函数只应进行持久性异步调用。An orchestrator function should make only durable async calls. 应该由活动函数进行任何其他异步 API 调用。Activity functions should make any other async API calls.
异步 JavaScript 函数Async JavaScript functions 不能将 JavaScript 业务流程协调程序函数声明为 async,因为 node.js 运行时不保证异步函数是确定性的。You can't declare JavaScript orchestrator functions as async because the node.js runtime doesn't guarantee that asynchronous functions are deterministic. 将 JavaScript 业务流程协调程序函数声明为同步的生成器函数。Declare JavaScript orchestrator functions as synchronous generator functions.
线程 APIThreading APIs Durable Task Framework 在单个线程上运行业务流程协调程序代码,不能与任何其他线程交互。The Durable Task Framework runs orchestrator code on a single thread and can't interact with any other threads. 将新线程引入业务流程的执行中可能导致非确定性执行或死锁。Introducing new threads into an orchestration's execution can result in nondeterministic execution or deadlocks. 在绝大多数情况下,业务流程协调程序函数不应使用线程 API。Orchestrator functions should almost never use threading APIs. 例如,在 .NET 中,应避免使用 ConfigureAwait(continueOnCapturedContext: false)。这样可以确保任务继续在业务流程协调程序函数的原始 SynchronizationContext 上运行。For example, in .NET, avoid using ConfigureAwait(continueOnCapturedContext: false); this ensures task continuations run on the orchestrator function's original SynchronizationContext. 如果需要使用此类 API,请将其使用限制为活动函数。If such APIs are necessary, limit their use to only activity functions.
静态变量Static variables 避免在业务流程协调程序函数中使用非常量静态变量,因为其值可能随时间而变,导致非确定性的运行时行为。Avoid using nonconstant static variables in orchestrator functions because their values can change over time, resulting in nondeterministic runtime behavior. 使用常量,或者将静态变量的使用限制为活动函数。Use constants, or limit the use of static variables to activity functions.
环境变量Environment variables 请勿在业务流程协调程序函数中使用环境变量。Don't use environment variables in orchestrator functions. 其值可能随时间而变,导致非确定性的运行时行为。Their values can change over time, resulting in nondeterministic runtime behavior. 环境变量只能在客户端函数或活动函数内部引用。Environment variables must be referenced only from within client functions or activity functions.
无限循环Infinite loops 请避免在业务流程协调程序函数中出现无限循环。Avoid infinite loops in orchestrator functions. 由于 Durable Task Framework 在业务流程函数的执行过程中会保存执行历史记录,无限循环可能会导致业务流程协调程序实例耗尽内存。Because the Durable Task Framework saves execution history as the orchestration function progresses, an infinite loop can cause an orchestrator instance to run out of memory. 对于无限循环方案,可使用 .NET 中的 ContinueAsNew 或 JavaScript 中的 continueAsNew 等 API 来重启函数执行,并丢弃以前的执行历史记录。For infinite loop scenarios, use APIs like ContinueAsNew in .NET or continueAsNew in JavaScript to restart the function execution and to discard previous execution history.

尽管这些约束看起来很难应用,但其实并不难遵守。Although applying these constraints might seem difficult at first, in practice they're easy to follow.

Durable Task Framework 会尝试检测上述规则的违规。The Durable Task Framework attempts to detect violations of the preceding rules. 如果发现违规,该框架将引发 NonDeterministicOrchestrationException 异常。If it finds a violation, the framework throws a NonDeterministicOrchestrationException exception. 但是,此检测行为不会捕获所有违规,因此请不要对它有依赖。However, this detection behavior won't catch all violations, and you shouldn't depend on it.

版本控制Versioning

持久性业务流程可能会连续运行数天、数月、数年,甚至会永久执行。A durable orchestration might run continuously for days, months, years, or even eternally. 对 Durable Functions 应用进行代码更新时,如果此类更新影响尚未完成的业务流程,则可能会中断业务流程的重播行为。Any code updates made to Durable Functions apps that affect unfinished orchestrations might break the orchestrations' replay behavior. 因此,在更新代码时请仔细进行计划,这很重要。That's why it's important to plan carefully when making updates to code. 有关如何进行代码版本控制的更详细说明,请参阅版本控制一文。For a more detailed description of how to version your code, see the versioning article.

持久任务Durable tasks

Note

本部分介绍 Durable Task Framework 的内部实现详细信息。This section describes internal implementation details of the Durable Task Framework. 在不了解这些信息的情况下也可以使用 Durable Functions。You can use durable functions without knowing this information. 本部分旨在帮助读者了解重播行为。It is intended only to help you understand the replay behavior.

可在业务流程协调程序函数中安全等待的任务有时称为“持久任务”。 Tasks that can safely wait in orchestrator functions are occasionally referred to as durable tasks. 这些任务由 Durable Task Framework 创建和管理。The Durable Task Framework creates and manages these tasks. 示例包括 .NET 业务流程协调程序函数中的 CallActivityAsyncWaitForExternalEventCreateTimer 返回的任务。Examples are the tasks returned by CallActivityAsync, WaitForExternalEvent, and CreateTimer in .NET orchestrator functions.

可以在 .NET 中使用 TaskCompletionSource 对象的列表对这些持久任务进行内部管理。These durable tasks are internally managed by a list of TaskCompletionSource objects in .NET. 在重播期间,这些任务在业务流程协调程序代码执行过程中予以创建。During replay, these tasks are created as part of orchestrator code execution. 在调度程序枚举相应历史记录事件时,这些任务将会完成。They're finished as the dispatcher enumerates the corresponding history events.

这些任务通过单个线程以同步方式执行,直至重播完所有历史记录。The tasks are executed synchronously using a single thread until all the history has been replayed. 在历史记录重播结束时尚未完成的任务会执行相应的操作。例如,可能会将一条消息排队,以调用活动函数。Durable tasks that aren't finished by the end of history replay have appropriate actions carried out. For example, a message might be enqueued to call an activity function.

本部分的运行时行为说明应可帮助你理解业务流程协调程序函数为何不能在非持久任务中使用 awaityieldThis section's description of runtime behavior should help you understand why an orchestrator function can't use await or yield in a nondurable task. 有两个原因:调度程序线程无法等待该任务完成,并且该任务发出的任何回调可能会损坏业务流程协调程序函数的跟踪状态。There are two reasons: the dispatcher thread can't wait for the task to finish, and any callback by that task might potentially corrupt the tracking state of the orchestrator function. 进行某些运行时检查是为了帮助检测此类违规。Some runtime checks are in place to help detect these violations.

若要详细了解 Durable Task Framework 如何执行业务流程协调程序函数,请查阅 GitHub 上的持久任务源代码To learn more about how the Durable Task Framework executes orchestrator functions, consult the Durable Task source code on GitHub. 具体而言,请参阅 TaskOrchestrationExecutor.csTaskOrchestrationContext.csIn particular, see TaskOrchestrationExecutor.cs and TaskOrchestrationContext.cs.

后续步骤Next steps