Durable Functions 中的计时器 (Azure Functions)
Artículo 12/20/2023
5 colaboradores
Comentarios
En este artículo
Durable Functions 提供了供在业务流程协调程序函数中使用的“持久计时器”,这些计时器用来为异步操作实现延迟或设置超时。 应在业务流程协调程序函数中使用持久计时器,而不是在语言中内置的“休眠”或“延迟”API 中使用。
持久计时器是使用提供语言的相应“创建计时器”API 创建的任务,如下所示,并采用截止日期或持续时间作为参数。
// Put the orchestrator to sleep for 72 hours
DateTime dueTime = context.CurrentUtcDateTime.AddHours(72);
await context.CreateTimer(dueTime, CancellationToken.None);
// Put the orchestrator to sleep for 72 hours
// Note that DateTime comes from the "luxon" module
const deadline = DateTime.fromJSDate(context.df.currentUtcDateTime, {zone: 'utc'}).plus({ hours: 72 });
yield context.df.createTimer(deadline.toJSDate());
# Put the orchestrator to sleep for 72 hours
due_time = context.current_utc_datetime + timedelta(hours=72)
durable_timeout_task = context.create_timer(due_time)
# Put the orchestrator to sleep for 72 hours
$duration = New-TimeSpan -Hours 72
Start-DurableTimer -Duration $duration
// Put the orchestrator to sleep for 72 hours
ctx.createTimer(Duration.ofHours(72)).await();
当“await”计时器任务时,业务流程协调程序函数将休眠到指定的过期时间。
注意
在等待计时器任务过期的同时,业务流程将继续处理其他传入事件。
计时器限制
创建在下午 4:30 UTC 过期的计时器时,基础 Durable Task Framework 会将一条仅在下午 4:30 UTC 才变得可见的消息排入队列。 如果在此期间将函数应用缩减到零个实例,则新可见的计时器消息将确保在相应的 VM 上再次激活函数应用。
注意
对于 JavaScript、Python 和 PowerShell 应用,持久计时器限制为 6 天。 若要解决此限制,可以使用 while
循环中的计时器 API 来模拟较长的延迟。 最新版 .NET 和 Java 应用支持任意长计时器。
根据所使用 SDK 和存储提供程序 的版本,可以使用一系列较短的计时器(例如 3 天的持续时间)在内部实现 6 天或更长时间的长计时器,直到达到所需的到期时间。 这可以在基础数据存储中观察到,但不会影响业务流程行为。
请勿使用内置日期/时间 API 来获取当前时间。 计算计时器到期的未来日期时,请始终使用业务流程协调程序函数的当前时间 API。 有关详细信息,请参阅业务流程协调程序函数代码约束 一文。
延迟的用法
下面的示例演示了如何使用持久计时器来延迟执行。 示例连续 10 天每天都会发出账单通知。
[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 版本 一文。
const df = require("durable-functions");
const { DateTime } = require("luxon");
module.exports = df.orchestrator(function*(context) {
for (let i = 0; i < 10; i++) {
const deadline = DateTime.fromJSDate(context.df.currentUtcDateTime, {zone: 'utc'}).plus({ days: 1 });
yield context.df.createTimer(deadline.toJSDate());
yield context.df.callActivity("SendBillingEvent");
}
});
import azure.functions as func
import azure.durable_functions as df
from datetime import datetime, timedelta
def orchestrator_function(context: df.DurableOrchestrationContext):
for i in range(0, 9):
deadline = context.current_utc_datetime + timedelta(days=1)
yield context.create_timer(deadline)
yield context.call_activity("SendBillingEvent")
main = df.Orchestrator.create(orchestrator_function)
param($Context)
for ($num = 0 ; $num -le 9 ; $num++){
$expiryTime = New-TimeSpan -Days 1
$timerTask = Start-DurableTimer -Duration $expiryTime
Invoke-DurableActivity -FunctionName 'SendBillingEvent'
}
@FunctionName("BillingIssuer")
public String billingIssuer(
@DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) {
for (int i = 0; i < 10; i++) {
ctx.createTimer(Duration.ofDays(1)).await();
ctx.callActivity("SendBillingEvent").await();
}
return "done";
}
警告
请避免在业务流程协调程序函数中出现无限循环。 有关如何安全有效地实现无限循环方案的信息,请参阅永久业务流程 。
超时的用法
此示例演示了如何使用持久计时器来实现超时。
[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 版本 一文。
const df = require("durable-functions");
const { DateTime } = require("luxon");
module.exports = df.orchestrator(function*(context) {
const deadline = DateTime.fromJSDate(context.df.currentUtcDateTime, {zone: 'utc'}).plus({ seconds: 30 });
const activityTask = context.df.callActivity("GetQuote");
const timeoutTask = context.df.createTimer(deadline.toJSDate());
const winner = yield context.df.Task.any([activityTask, timeoutTask]);
if (winner === activityTask) {
// success case
timeoutTask.cancel();
return true;
}
else
{
// timeout case
return false;
}
});
import azure.functions as func
import azure.durable_functions as df
from datetime import datetime, timedelta
def orchestrator_function(context: df.DurableOrchestrationContext):
deadline = context.current_utc_datetime + timedelta(seconds=30)
activity_task = context.call_activity("GetQuote")
timeout_task = context.create_timer(deadline)
winner = yield context.task_any([activity_task, timeout_task])
if winner == activity_task:
timeout_task.cancel()
return True
elif winner == timeout_task:
return False
main = df.Orchestrator.create(orchestrator_function)
param($Context)
$expiryTime = New-TimeSpan -Seconds 30
$activityTask = Invoke-DurableActivity -FunctionName 'GetQuote'-NoWait
$timerTask = Start-DurableTimer -Duration $expiryTime -NoWait
$winner = Wait-DurableTask -Task @($activityTask, $timerTask) -Any
if ($winner -eq $activityTask) {
Stop-DurableTimerTask -Task $timerTask
return $True
}
else {
return $False
}
@FunctionName("TryGetQuote")
public boolean tryGetQuote(
@DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) {
Task<Double> activityTask = ctx.callActivity("GetQuote", Double.class);
Task<Void> timerTask = ctx.createTimer(Duration.ofSeconds(30));
Task<?> winner = ctx.anyOf(activityTask, timerTask);
if (winner == activityTask) {
// success case
return true;
} else {
// timeout case
return false;
}
}
警告
在 .NET、JavaScript、Python 和 PowerShell 中,如果代码不会等待任何创建的持久计时器完成,则必须取消它们。 有关如何取消等待中的计时器,请参阅上面的示例。 在所有未完成任务(包括持久计时器任务)都完成或取消之前,Durable Task Framework 不会将业务流程的状态更改为“已完成”。
此使用 when-any 模式的取消机制不会终止正在进行的活动函数执行或子业务流程执行。 它只是允许业务流程协调程序函数忽略结果并继续运行。 如果函数应用使用了消耗计划,则还需要为已放弃的活动函数消耗的任何时间和内存付费。 默认情况下,在消耗计划中运行的函数有五分钟的超时。 如果超出了此限制,则会回收 Azure Functions 主机以停止所有执行并防止出现费用失控的情况。 函数超时是可配置的 。
后续步骤