我因为这个功能彻底从 AWS Lambda 迁移到 Azure Functions
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 环境中,这意味着:
- 将待处理状态存储在某处。
- 设置一个 API Gateway 端点来接收批准。
- 轮询或使用回调来恢复工作流。
- 处理超时(如果他们永远不响应怎么办?)。
- 处理所有边缘情况。
我为这个功能写了 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 支持五个关键模式,正好解决了我当时遇到的痛点:
-
函数链(Function Chaining) – 按顺序调用函数,将前一个函数的输出作为下一个函数的输入。编排器会自动管理状态。
-
扇出/扇入(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(); -
异步 HTTP API(Async HTTP APIs) – 长时间运行的操作内置状态轮询。框架会自动提供状态端点。
-
监控模式(Monitor Pattern) – 轮询工作流定期检查条件,直至满足某个事件。非常适合等待外部系统。
-
人工交互(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:
- 最坏情况: 你学到新东西。
- 最好情况: 你会想为什么不早点切换。
这就是我现在的状态。说实话?我不打算回头。