本地运行Azure Durable Function启动时活动函数重复执行问题
问题描述
现有一个HTTP触发的Azure Durable Function,包含名为ExecuteWork的编排触发器,以及HandleClaimsForms、HandleApplicationForms两个活动触发器,用途是处理Blob存储容器中的PDF文件。本地运行时,未收到任何调用请求的情况下,函数启动阶段就会自动执行HandleClaimsForms4到5次。
运行日志
Functions: Function1: [GET,POST] http://localhost:7071/api/Function1 ExecuteWork: orchestrationTrigger HandleApplicationForms: activityTrigger HandleClaimsForms: activityTrigger [2022-06-07T12:39:44.587Z] Executing 'HandleClaimsForms' (Reason='(null)', Id=c45878fe-35c8-4a57-948e-0b43da969427) [2022-06-07T12:39:44.587Z] Executing 'HandleClaimsForms' (Reason='(null)', Id=0fb9644d-6748-4791-96cf-a92f6c161a97) [2022-06-07T12:39:44.587Z] Executing 'HandleClaimsForms' (Reason='(null)', Id=9a39a169-a91d-4524-b5e5-63e6226f70ec) [2022-06-07T12:39:44.587Z] Executing 'HandleClaimsForms' (Reason='(null)', Id=b3697f6b-7c96-4497-826f-3894359ff361) [2022-06-07T12:39:44.587Z] Executing 'HandleClaimsForms' (Reason='(null)', Id=3ca3bbce-1657-453b-a5b3-e9dbdb940302)
现有函数实现
HTTP入口函数
[FunctionName("Function1")] public async Task<IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, [DurableClient] IDurableOrchestrationClient starter, ILogger log) { string instanceID = await starter.StartNewAsync("ExecuteWork"); return starter.CreateCheckStatusResponse(req, instanceID); }
编排触发器函数
[FunctionName("ExecuteWork")] public async Task<bool> ProcessForms( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { bool success = true; try { await context.CallActivityAsync("HandleClaimsForms", success); await context.CallActivityAsync("HandleApplicationForms", success); return success; } catch (Exception err) { log.LogInformation($"The following error was thrown: {err}"); success = false; return success; } }
HandleClaimsForms活动函数
public async Task<bool> ProcessClaimsForms( [ActivityTrigger]bool success) { await _docHandler.Handle(); return success; }
HandleApplicationForms活动函数
[FunctionName("HandleApplicationForms")] public async Task<bool> ProcessApplicationForms( [ActivityTrigger]bool success) { await _appHandler.HandleJsonApplicationFormAsync(); return success; }
问题原因
两个问题共同导致该现象:
- 本地存储模拟器残留历史实例:本地调试Durable Function时如果强制中断宿主,未执行完成的编排实例状态会持久化在Azurite/旧版Azure存储模拟器中。下次函数宿主启动时,会自动加载所有未完成的编排实例恢复执行,不需要等待新的HTTP请求触发。日志中出现的4-5次执行,就是之前调试残留的多个未完成实例恢复调度的结果。
- 活动函数特性缺失:
HandleClaimsForms对应的ProcessClaimsForms方法上没有加[FunctionName("HandleClaimsForms")]特性,函数运行时无法正确匹配活动名称和方法,会导致重放调度时出现重复执行的异常行为。
修复方案
- 清理本地残留的编排状态:最简单的方式是修改
local.settings.json中AzureWebJobsStorage的连接字符串配置,给存储账户名/密钥加随机后缀,让函数启动时使用全新的空存储上下文,就不会加载历史残留实例;如果用Azurite也可以直接删除Azurite工作目录下的表、队列持久化文件完成清理。 - 给
HandleClaimsForms方法补全缺失的FunctionName特性,修正后的代码如下:
[FunctionName("HandleClaimsForms")] public async Task<bool> ProcessClaimsForms( [ActivityTrigger]bool success) { await _docHandler.Handle(); return success; }
- 可选优化:编排函数中使用重放安全的Logger,避免编排重放阶段产生重复冗余日志,把编排函数中的日志创建逻辑替换为
var replayLog = context.CreateReplaySafeLogger(log);,所有日志输出都用replayLog实例写入即可。
内容的提问来源于stack exchange,提问作者LiamBaisley
相关产品推荐
相关产品推荐

