我因为这个功能彻底从 AWS Lambda 迁移到 Azure Functions

发布: (2025年12月21日 GMT+8 02:27)
10 min read
原文: Dev.to

Source: Dev.to

让我给你描绘一下我在切换之前的生活画面。

我在构建一个订单处理系统。并没有什么特别疯狂的——接收订单、验证库存、处理付款、更新数据库、发送通知。这类听起来很简单的工作,直到你尝试让它变得可靠才会发现问题。

使用 AWS Lambda 时,每个函数都是无状态的。这本身就是设计如此,对于简单的使用场景来说其实没问题。但事情就在这里变得混乱了:

状态管理噩梦

为了跟踪订单在流水线中的位置,我需要 DynamoDB。为了在函数之间传递数据,我需要 SQS。为了编排整个流程,我需要 Step Functions。突然,我的“简单”工作流涉及到:

  • 5 个 Lambda 函数
  • 2 个 DynamoDB 表(一个用于状态,一个用于死信跟踪)
  • 3 条 SQS 队列
  • 1 台 Step Functions 状态机
  • 一只鹧鸪挂在梨树上(好吧,最后那项可能不算)

每当出现故障时,我必须在多个服务的日志中追踪。每当我想添加一个步骤时,我都必须更新 Step Functions 的定义,而它使用的是基于 JSON 的 Amazon States Language。感觉自己在构建基础设施,而不是功能。

人机交互问题

事情真正崩溃的地方在这里。我们需要审批工作流。必须有经理在一定金额以上的订单得到批准后才能处理。

在 Lambda 环境中,这意味着:

  1. 将待处理状态存储在某处。
  2. 设置一个 API Gateway 端点来接收批准。
  3. 轮询或使用回调来恢复工作流。
  4. 处理超时(如果他们永远不响应怎么办?)。
  5. 处理所有边缘情况。

我为这个功能写了 400 行代码。说实话?它非常脆弱。我总是担心会有什么东西出问题。

Source:

发现 Durable Functions(改变一切的时刻)

我在研究替代方案时偶然发现了 Azure Durable Functions。起初,我持怀疑态度——又是一个供应商承诺的魔法?当然。

但随后我看到了代码示例,我并不是夸大其词地说,我的下巴掉了下来。

Durable Functions 中的人机交互工作流

[FunctionName("ApprovalWorkflow")]
public static async Task RunOrchestrator(
    [OrchestrationTrigger] IDurableOrchestrationContext context)
{
    var order = context.GetInput();

    // Check if approval is needed
    if (order.Amount > 1000)
    {
        // Wait for external event – could be hours or days
        var approved = await context.WaitForExternalEvent("ApprovalReceived");

        if (!approved)
        {
            return false;
        }
    }

    // Continue with processing
    await context.CallActivityAsync("ProcessOrder", order);
    await context.CallActivityAsync("SendNotification", order);

    return true;
}

就是这样。框架负责状态、超时和持久化。如果函数主机重启,它会从中断的地方继续执行。

400 行 AWS 代码变成了约 20 行 Azure 代码。 我不是在编造。

Source:

五种让我信服的模式

Durable Functions 支持五个关键模式,正好解决了我当时遇到的痛点:

  1. 函数链(Function Chaining) – 按顺序调用函数,将前一个函数的输出作为下一个函数的输入。编排器会自动管理状态。

  2. 扇出/扇入(Fan‑Out/Fan‑In) – 需要并行处理 100 条记录并随后聚合结果吗?Durable Functions 原生支持——无需队列,也无需手动跟踪。

    var tasks = new List>();
    foreach (var item in items)
    {
        tasks.Add(context.CallActivityAsync("ProcessItem", item));
    }
    
    var results = await Task.WhenAll(tasks);
    var total   = results.Sum();
  3. 异步 HTTP API(Async HTTP APIs) – 长时间运行的操作内置状态轮询。框架会自动提供状态端点。

  4. 监控模式(Monitor Pattern) – 轮询工作流定期检查条件,直至满足某个事件。非常适合等待外部系统。

  5. 人工交互(Human Interaction) – 等待外部事件,同时支持超时和升级机制。这是彻底改变我使用体验的功能。

我的迁移之旅

阶段持续时间我做了什么
第 1‑2 周:学习曲线2 周学习编排器模型。了解到编排函数在每次恢复时都会从头重新播放,但运行时保证确定性执行。
第 3‑4 周:试点迁移2 周将最简单的工作流重写为 Durable Functions,和 AWS 版本一起部署,并比较结果。
第 5‑6 周:复杂工作流2 周迁移完整的订单处理系统。Durable Functions 版本更短、更易调试;我可以查看编排历史,重放失败实例,准确看到每一步发生了什么。
第 7‑8 周:全面切换2 周下线 AWS 基础设施——Step Functions、DynamoDB 表、SQS 队列——告别复杂性。

实际结果

老实说,真正提升的地方如下:

  • 代码量减少 60 %(从约 400 行降至约 150 行)。
  • 开发周期加快 40 %,因为不再需要管理多个服务。
  • 可靠性提升:状态持久化和自动重试已内置。
  • 调试更简便:Azure 门户提供可视化编排时间线,并可重放失败实例。
  • 运营成本降低:需要运行和监控的服务更少。

如果你仍在为一堆 Lambda、Step Functions、SQS 和 DynamoDB 纠缠,只为实现一个简单的审批流程而头疼,不妨看看 Azure Durable Functions。它可能帮你省去数月的架构痛点——以及大量深夜的控制台排查。

# Benefits of Moving to Azure Durable Functions

减少代码量

订单处理工作流从多个服务中大约 1,200 行 减少到 Durable Functions 中约 450 行
代码更少意味着 bug 更少,维护更容易。

简化架构

  • Before: 5 AWS services
  • After: 2 Azure services (Functions + Storage)

更小的思维模型可以加快调试速度。

更好的可观测性

Durable Functions 提供每个编排的内置历史记录。
您可以准确看到每个步骤的执行时间、输入/输出以及失败发生的位置。

成本降低

消除 Step Functions(按状态转换计费)并减少 DynamoDB 使用,使月账单下降约 ≈ 40 %
您的实际情况可能有所不同,但对于有状态工作流来说,这种计费模式通常更划算。


更快的开发

以前需要一周的功能现在只需一天。
抽象层次恰到好处——足够高以避免样板代码,又足够低以保持控制。

这真的完美吗?

不。 以下是权衡:

  • 重放模型: 需要严格遵守;在编排器内部避免使用非确定性代码(例如 DateTime.Now)。
  • 迁移成本: 如果你已经深入使用 AWS 生态系统并熟悉 Step Functions,对于简单的使用场景,迁移可能不值得。
  • 简单工作负载: 对于真正无状态的函数,普通的 Lambda 或 Azure Functions(不使用 Durable)已经足够。

结论: 对于复杂的工作流、人机交互、长时间运行的过程或多步骤协调,Durable Functions 是改变游戏规则的利器。

最后的思考

我在自言自语——也在对那些凌晨 2 点还在与不必要的复杂性搏斗的人说。

有时合适的工具会产生天壤之别。对我而言,Azure Durable Functions 让我专注于业务逻辑,而不是基础设施的繁琐细节。

如果你遇到了我在使用 Lambda 时碰到的同样的瓶颈,不妨看看 Durable Functions:

  • 最坏情况: 你学到新东西。
  • 最好情况: 你会想为什么不早点切换。

这就是我现在的状态。说实话?我不打算回头。

Back to Blog

相关文章

阅读更多 »