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

Azure Durable Functions SQL异常处理:终止执行及日志查询方案

Azure Durable Functions SQL操作异常处理实现方案

Activity层异常处理逻辑

首先修正你示例代码中的基础拼写问题:.NET中SQL客户端类为SqlConnection/SqlCommand,执行方法为ExecuteReader,且using代码块会在执行结束后自动释放连接资源,无需手动调用conn.Close()。
异常处理核心逻辑:

  • 将所有SQL连接、执行逻辑包裹在try/catch块中,优先捕获SQL操作专属的SqlException类型,覆盖连接失败、存储过程语法错误、执行逻辑报错、权限不足等所有数据库侧异常
  • 捕获到异常后,附带业务上下文信息(业务标识、执行参数、错误编号)后直接抛出,不做异常吞没,Activity会立即终止执行,异常会自动透传到上层编排函数
  • 非SQL类的通用异常(比如参数空引用、序列化失败)同样捕获后透传,避免静默失败

完整Activity代码示例:

using Microsoft.Data.SqlClient;
using System.Data;

// 存储过程执行成功返回结构
public class SpExecutionOutput
{
    public List<Dictionary<string, object>> QueryRecords { get; set; }
}

[FunctionName("Activity_ExecuteStoredProcedure")]
public static async Task<SpExecutionOutput> ExecuteStoredProcedure(
    [ActivityTrigger] (string BusinessId, Dictionary<string, object> SpParameters) input,
    ILogger log)
{
    var connStr = Environment.GetEnvironmentVariable("sqldb_connection");
    try
    {
        using var conn = new SqlConnection(connStr);
        await conn.OpenAsync();
        using var cmd = new SqlCommand("Stored_procedure", conn)
        {
            CommandType = CommandType.StoredProcedure,
            CommandTimeout = 30 // 根据存储过程执行时长调整超时时间
        };
        // 自动填充存储过程入参
        if (input.SpParameters != null)
        {
            foreach (var param in input.SpParameters)
            {
                cmd.Parameters.AddWithValue(param.Key, param.Value ?? DBNull.Value);
            }
        }

        var result = new List<Dictionary<string, object>>();
        using var reader = await cmd.ExecuteReaderAsync();
        while (await reader.ReadAsync())
        {
            var row = new Dictionary<string, object>();
            for (int i = 0; i < reader.FieldCount; i++)
            {
                row.Add(reader.GetName(i), reader.IsDBNull(i) ? null : reader.GetValue(i));
            }
            result.Add(row);
        }
        return new SpExecutionOutput { QueryRecords = result };
    }
    catch (SqlException ex)
    {
        var errorMsg = $"存储过程执行失败,业务ID:{input.BusinessId},SQL错误号:{ex.Number},错误信息:{ex.Message}";
        log.LogError(ex, errorMsg);
        // 抛出异常终止Activity执行
        throw new InvalidOperationException(errorMsg, ex);
    }
    catch (Exception ex)
    {
        var errorMsg = $"存储过程执行出现非预期错误,业务ID:{input.BusinessId},错误信息:{ex.Message}";
        log.LogError(ex, errorMsg);
        throw new InvalidOperationException(errorMsg, ex);
    }
}

编排层异常捕获与重试

编排函数调用Activity时,通过try/catch捕获异常,同时可利用Durable Functions原生的重试策略对可重试错误(比如连接超时、死锁)做自动重试,重试耗尽后进入失败处理逻辑,不会执行后续业务流程。
示例代码:

[FunctionName("Orchestrator_MainBusinessFlow")]
public static async Task RunMainFlow(
    [OrchestrationTrigger] IDurableOrchestrationContext context,
    ILogger log)
{
    var businessInput = context.GetInput<(string BusinessId, Dictionary<string, object> SpParameters)>();
    try
    {
        // 重试策略:最多重试3次,首次间隔5秒,后续间隔按指数递增
        var retryPolicy = new RetryOptions(TimeSpan.FromSeconds(5), 3)
        {
            BackoffCoefficient = 2,
            // 仅对连接错误、死锁错误重试,存储过程逻辑错误、语法错误不重试
            Handle = ex => ex.InnerException is SqlException sqlEx 
                && (sqlEx.Number == 1205 || sqlEx.Number == 10060 || sqlEx.Number == 40197)
        };

        // 调用带重试的Activity,执行失败且重试耗尽时会抛出异常
        var spResult = await context.CallActivityWithRetryAsync<SpExecutionOutput>(
            "Activity_ExecuteStoredProcedure", 
            retryPolicy, 
            businessInput);

        // 仅存储过程执行成功才会走到后续流程
        await context.CallActivityAsync("Activity_NextBusinessStep", spResult);
    }
    catch (Exception ex)
    {
        // 构造失败信息
        var failureInfo = new
        {
            BusinessId = businessInput.BusinessId,
            InstanceId = context.InstanceId,
            ErrorMessage = ex.Message,
            FailedAtUtc = context.CurrentUtcDateTime,
            StackTrace = ex.StackTrace
        };
        // 触发失败处理:持久化错误、通知用户
        await context.CallActivityAsync("Activity_ProcessFailure", failureInfo);
        // 标记编排实例为失败状态
        throw;
    }
}

失败信息存储方案

可选三种主流存储方式,可根据实际运维需求选择:

  • 自定义业务表存储:在现有Azure SQL中新建执行失败记录表,在Activity_ProcessFailure中写入失败信息,表字段建议包含业务ID、编排实例ID、错误信息、异常堆栈、失败时间、处理状态。这种方式最贴合业务查询习惯。
  • Durable Functions原生实例存储:Durable Functions默认会将所有编排实例的状态、异常信息存在TaskHub关联的Azure Storage账号中,无需额外开发存储逻辑,所有失败信息自动持久化。
  • Application Insights存储:为函数应用配置Application Insights后,所有异常、执行日志会自动上报,自带全链路追踪ID,适合问题排查。

失败记录检索方法

对应不同存储方式的检索路径:

  • 自定义表存储:直接通过SQL语句按业务ID、时间范围、处理状态筛选查询即可,示例:
    SELECT * FROM SpExecutionFailures 
    WHERE BusinessId = @QueryBusinessId 
    AND FailedAtUtc BETWEEN @StartTime AND @EndTime
    
  • 原生实例存储检索:
    • 通过Durable Functions客户端的GetStatusAsync方法,传入编排实例ID即可获取完整失败详情
    • 直接查询TaskHub对应的Instances表,筛选RuntimeStatus = 'Failed'的记录,可按实例ID、创建时间范围过滤
    • 通过Azure Portal的Durable Functions监控面板,按实例状态、时间筛选失败实例查看详情
  • Application Insights检索:在日志查询界面通过Kusto语句查询,示例:
    exceptions
    | where timestamp > ago(30d)
    | where operation_Name == "Activity_ExecuteStoredProcedure"
    | where customDimensions.BusinessId == "查询的业务ID"
    | project timestamp, message, customDimensions, stacktrace
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 04:01:07