重要
对进程内模型的支持将于 2026 年 11 月 10 日结束。 为获得完全支持,强烈建议将应用迁移到独立工作模型。
Durable Functions提供持久性计时器用于协调程序函数,以实现延迟或设置异步操作的超时。 在业务流程协调程序函数中使用持久计时器,而非语言中内置的 sleep 或 delay API。
持久任务 SDK 提供 持久计时器,用于在编排过程中使用,以实现延迟或为异步操作设置超时。 在业务流程中使用持久计时器,而非语言中内置的 sleep 或 delay API。
重要
目前,PowerShell Durable Task SDK 不可用。
持久计时器是使用相应 create timer API 为提供的语言创建的任务,如以下示例所示,并采用到期时间或持续时间作为参数。
隔离工作者模型
[Function("TimerOrchestration")]
public static async Task Run(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
// Put the orchestrator to sleep for 72 hours
DateTime dueTime = context.CurrentUtcDateTime.AddHours(72);
await context.CreateTimer(dueTime, CancellationToken.None);
}
进程内模型
[FunctionName("TimerOrchestration")]
public static async Task Run(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
// Put the orchestrator to sleep for 72 hours
DateTime dueTime = context.CurrentUtcDateTime.AddHours(72);
await context.CreateTimer(dueTime, CancellationToken.None);
}
// Put the orchestration to sleep for 72 hours
await context.CreateTimer(TimeSpan.FromHours(72), CancellationToken.None);
当你 await 计时器任务时,业务流程协调程序函数会休眠,直到指定的到期时间。
当你 await 计时器任务时,业务流程会休眠,直到指定的到期时间。
注意
业务流程在等待计时器任务过期时继续处理其他传入事件。
持久计时器的局限性
创建一个在 UTC 时间下午 4:30 到期的计时器时,基础的 Durable Task Framework 会排队一条消息,该消息只有在 UTC 时间下午 4:30 才可见。 如果函数应用同时缩减为零个实例,则新可见的计时器消息可确保函数应用在相应的 VM 上再次激活。
注意
- 对于 JavaScript、Python 和 PowerShell 应用,持久计时器限制为 6 天。 若要解决此限制,请使用循环中的
while计时器 API 来模拟更长的延迟。 最新的.NET和Java应用支持任意长的计时器。 - 根据所使用的 SDK 和 存储提供程序 的版本,在达到所需过期时间之前,可能会使用一系列较短的计时器(例如,三天的持续时间)在内部实现 6 天或更多天的长计时器。 此行为在基础数据存储中可观察,但不会影响业务流程行为。
- 请勿使用内置日期/时间 API 来获取当前时间。 计算计时器过期的未来日期时,请始终使用协调器函数的当前时间 API。 有关详细信息,请参阅业务流程协调程序函数代码约束一文。
创建一个在 UTC 时间下午 4:30 到期的计时器时,基础的 Durable Task Framework 会排队一条消息,该消息只有在 UTC 时间下午 4:30 才可见。 计时器消息可确保计时器过期时工作线程再次激活。
注意
- 指定长时间延迟(例如,几天或更多延迟)可能会导致创建多个内部管理的持久计时器。 协调代码不需要注意此行为。 但是,它可能在框架日志和存储的历史记录状态中可见。
- 请勿使用内置日期和时间 API 来获取当前时间。 计算计时器过期的未来日期时,请始终使用业务流程上下文的当前时间属性(如 .NET 中的
context.CurrentUtcDateTime、Python 中的ctx.current_utc_datetime,或在 JavaScript 中使用ctx.currentUtcDateTime)。
使用可靠计时器来设置延迟
以下示例演示如何使用持久计时器来延迟执行。 该示例每天发出一条计费通知 10 天。
隔离工作者模型
[Function("BillingIssuer")]
public static async Task Run(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
for (int i = 0; i < 10; i++)
{
DateTime deadline = context.CurrentUtcDateTime.Add(TimeSpan.FromDays(1));
await context.CreateTimer(deadline, CancellationToken.None);
await context.CallActivityAsync("SendBillingEvent");
}
}
进程内模型
[FunctionName("BillingIssuer")]
public static async Task Run(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
for (int i = 0; i < 10; i++)
{
DateTime deadline = context.CurrentUtcDateTime.Add(TimeSpan.FromDays(1));
await context.CreateTimer(deadline, CancellationToken.None);
await context.CallActivityAsync("SendBillingEvent");
}
}
注意
前面的 C# 示例面向 Durable Functions 2.x。 对于 Durable Functions 1.x,请使用 DurableOrchestrationContext 而不是 IDurableOrchestrationContext。 有关版本之间差异的详细信息,请参阅 Durable Functions 版本一文。
public class BillingIssuer : TaskOrchestrator<object?, string>
{
public override async Task<string> RunAsync(TaskOrchestrationContext context, object? input)
{
for (int i = 0; i < 10; i++)
{
await context.CreateTimer(TimeSpan.FromDays(1), CancellationToken.None);
await context.CallActivityAsync("SendBillingEvent");
}
return "done";
}
}
Warning
避免编排器函数中的无限循环。 有关如何安全高效地实现无限循环方案的信息,请参阅 永恒业务流程。
Warning
避免编排中的无限循环。 有关如何安全高效地实现无限循环方案的信息,请参阅 永恒业务流程。
对超时使用持久计时器
此示例演示如何使用持久计时器实现超时:
隔离工作者模型
[Function("TryGetQuote")]
public static async Task<bool> Run(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
TimeSpan timeout = TimeSpan.FromSeconds(30);
DateTime deadline = context.CurrentUtcDateTime.Add(timeout);
using var timeoutCts = new CancellationTokenSource();
Task activityTask = context.CallActivityAsync("GetQuote");
Task timeoutTask = context.CreateTimer(deadline, timeoutCts.Token);
Task winner = await Task.WhenAny(activityTask, timeoutTask);
if (winner == activityTask)
{
// Success case
timeoutCts.Cancel();
return true;
}
else
{
// Timeout case
return false;
}
}
进程内模型
[FunctionName("TryGetQuote")]
public static async Task<bool> Run(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
TimeSpan timeout = TimeSpan.FromSeconds(30);
DateTime deadline = context.CurrentUtcDateTime.Add(timeout);
using (var cts = new CancellationTokenSource())
{
Task activityTask = context.CallActivityAsync("GetQuote");
Task timeoutTask = context.CreateTimer(deadline, cts.Token);
Task winner = await Task.WhenAny(activityTask, timeoutTask);
if (winner == activityTask)
{
// success case
cts.Cancel();
return true;
}
else
{
// timeout case
return false;
}
}
}
注意
前面的 C# 示例面向 Durable Functions 2.x。 对于 Durable Functions 1.x,请使用 DurableOrchestrationContext 而不是 IDurableOrchestrationContext。 有关版本之间差异的详细信息,请参阅 Durable Functions 版本一文。
public class TryGetQuote : TaskOrchestrator<object?, bool>
{
public override async Task<bool> RunAsync(TaskOrchestrationContext context, object? input)
{
using var cts = new CancellationTokenSource();
Task<double> activityTask = context.CallActivityAsync<double>("GetQuote");
Task timeoutTask = context.CreateTimer(TimeSpan.FromSeconds(30), cts.Token);
Task winner = await Task.WhenAny(activityTask, timeoutTask);
if (winner == activityTask)
{
// success case
cts.Cancel();
return true;
}
else
{
// timeout case
return false;
}
}
}
Warning
在 .NET、JavaScript、Python 和 PowerShell 中,如果代码没有等待它们完成,请取消任何创建的持久计时器。 请参阅前面的示例,了解如何取消挂起的计时器。 在完成或取消所有未完成的任务(包括持久计时器任务)之前,Durable Task Framework 不会将业务流程的状态更改为“已完成”。
Warning
如果您的 SDK 支持计时器取消(例如 .NET),并且代码没有等待计时器完成,则请取消任何创建的持久计时器。 请参阅前面的示例,了解如何取消挂起的计时器。 在完成或取消所有未完成的任务(包括持久计时器任务)之前,Durable Task Framework 不会将业务流程的状态更改为“已完成”。
此取消机制使用 when-any 模式,不会终止目前正在执行的活动函数或子编排。 相反,它只是让业务流程协调程序函数忽略结果并继续前进。 如果您的函数应用使用消耗计划,即使是被放弃的活动函数消耗的时间和内存也会计费。 默认情况下,消耗计划中运行的函数超时为 5 分钟。 如果超过此限制,Azure Functions 主机将进行回收,以停止所有执行操作,并防止出现失控的计费情况。 函数超时是可配置的。
有关如何在协调程序函数中实现超时的更详细示例,请参阅 人类交互 文章。
这种使用 when-any 模式的取消机制不会终止正在进行的活动或子业务流程执行。 相反,它只是让编排忽略结果并继续。