You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Azure Functions:编排器本地业务逻辑是否需移至活动函数?

Azure Durable Functions:编排器内数据处理的最佳实践

不需要把所有业务逻辑都迁移到活动函数,核心是根据你的数据处理开销和重放影响来做权衡:

核心原理

Durable Orchestrator的重放机制会跳过已经完成的CallActivityAsync调用(这些操作的结果已被框架持久化),但编排器内的本地计算逻辑会在每次重放时重新执行。如果你的处理逻辑是轻量的(比如小规模数据的过滤、排序),重复执行的资源开销可以忽略,完全可以留在编排器中。

什么时候需要把逻辑移到活动函数

如果出现以下情况,建议将数据处理逻辑封装到活动函数:

  • 处理逻辑耗时较长:比如针对上万条以上数据的复杂过滤、聚合、排序,重复执行会浪费计算资源、延长整体执行时间
  • 处理逻辑依赖外部可变状态:比如读取数据库、调用第三方服务(后续扩展有这类依赖时,必须移到活动函数,避免重放时的不一致)
  • 需要持久化中间计算结果:活动函数的输出会被Durable Framework自动持久化,重放时直接复用结果,无需重新计算

优化方案示例

如果你的数据处理开销较大,可以将中间处理逻辑封装为活动函数,比如新增一个ProcessApiObjectsAsync活动函数:

[FunctionName("ProcessApiObjectsAsync")]
public IEnumerable<ApiObject> ProcessApiObjects([ActivityTrigger] IEnumerable<ApiObject> someObjects)
{
    var objectsStartWithA = someObjects
        .Where(x => !string.IsNullOrEmpty(x.Name) && x.Name.StartsWith("a", StringComparison.OrdinalIgnoreCase))
        .OrderBy(x => x.Name)
        .ToList();

    var objectsStartWithB = someObjects
        .Where(x => !string.IsNullOrEmpty(x.Name) && x.Name.StartsWith("b", StringComparison.OrdinalIgnoreCase))
        .OrderBy(x => x.Name)
        .ToList();

    return objectsStartWithA.Union(objectsStartWithB);
}

然后在编排器中调用这个活动函数,替代本地计算:

[FunctionName(FunctionName)]
public async Task ExecuteAsync([OrchestrationTrigger] IDurableOrchestrationContext context)
{
    var logger = context.CreateReplaySafeLogger(_logger);
    logger.LogInformation("Process started");

    var someObjects = (await context.CallActivityAsync<IEnumerable<ApiObject>>("GetApiObjectsAsync", null)).ToList();
    // 调用活动函数处理数据,重放时直接复用结果
    var objectsToCompare = await context.CallActivityAsync<IEnumerable<ApiObject>>("ProcessApiObjectsAsync", someObjects);

    var someOtherObjects = (await context.CallActivityAsync<IEnumerable<ApiObject>>("GetOtherApiObjectsAsync", null)).ToList();
    // 如果filteredObjects的处理开销大,同样可以封装为活动函数
    var filteredObjects = someOtherObjects
            .Where(x => objectsToCompare.All(z => z.Id != x.Id)) // 修正原代码笔误:z.Id != z.Id 改为 z.Id != x.Id
            .OrderBy(x => x.Id)
            .ToList();

    await context.CallActivityAsync("SaveObjects", filteredObjects);

    logger.LogInformation("Process finished");
}

额外注意点

  • 原代码中有一处笔误:Where(x => objectsToCompare.All(z => z.Id != z.Id))逻辑完全错误,应改为z.Id != x.Id才能实现预期的过滤效果
  • 编排器内的本地计算必须是确定性的(基于固定输入总能得到相同输出),否则重放会导致状态不一致,你的场景满足这个要求,所以无需担心功能问题

内容的提问来源于stack exchange,提问作者Prog

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 00:01:39