Multi-region disaster recovery for Amazon API Gateway WebSocket APIs

TL;DR · AI 摘要
Multi-region disaster recovery for Amazon API Gateway WebSocket APIs AWS Architecture Blog Multi-region disaster recover...
核心要点
- 主题聚焦:Multi-region disaster recovery for Amazon API Ga
- 来源:AWS Architecture Blog,建议结合原文判断细节。
- AI 分析暂不可用,本条为保底评分与摘要。
Amazon API Gateway WebSocket API 的多区域灾难恢复 | AWS 架构博客
Amazon API Gateway WebSocket API 的多区域灾难恢复
WebSocket 连接为网络上最具互动性的体验提供了基础:金融交易应用实时传输市场数据、多人协作编辑工具共同处理同一文档、游戏排行榜实时更新以及 IoT 仪表板监控数千设备。这些应用依赖于客户端与服务器之间的持久双向通信。
使用 WebSocket 连接的实时应用在区域故障期间面临独特挑战。WebSocket 连接是持久的且绑定到特定 AWS 区域。区域故障会导致所有活动连接中断。对于需要在这些连接间维护共享状态的应用(如实时拍卖或协作编辑器),用户会同时失去连接和会话上下文。本文介绍一种基于 Amazon API Gateway WebSocket API 的多区域灾难恢复设计模式,可在故障切换期间自动重新连接客户端并恢复状态。
本文将指导您如何使用 Amazon Route 53 实现基于 DNS 的故障转移、Amazon DynamoDB 全局表实现跨区域状态复制、金丝雀健康探测实现独立于流量的故障检测,以及客户端重连逻辑来在保持应用状态的同时从区域故障中恢复。
解决方案概述
该模式创建了一个主动-被动多区域架构,主区域负责 WebSocket 连接,并在灾难发生时自动切换到次级区域。核心思想是将应用状态与连接本身分离,使客户端在重新连接后能够恢复会话。
架构在两个区域(主区域和次级区域)部署相同的 WebSocket API 堆栈。两个区域通过 DynamoDB 全局表共享应用状态,确保次级区域始终准备就绪处理流量。一个自定义域名同时指向两个区域。Amazon Route 53 故障转移路由策略在正常情况下将流量导向主区域。当主区域变为不健康状态时,Route 53 会自动将 DNS 解析切换到次级区域。客户端重新连接到相同域名后无需更改配置即可恢复操作。
图 1 展示了架构及正常运行和故障转移流程。
图 1:多区域 WebSocket API 故障转移架构
- 用户打开应用,应用建立到应用域名的 WebSocket 连接。
- Amazon Route 53 使用故障转移路由策略解析域名。正常情况下返回主区域端点。
- WebSocket 连接到达主区域的 API Gateway,触发 AWS Lambda 函数处理每个连接事件和消息。
- Lambda 从 DynamoDB 读取和写入应用状态。已连接客户端通过开放的 WebSocket 连接接收更新。
- DynamoDB 全局表异步将应用状态复制到次级区域,保持其随时准备处理流量的状态。
- 独立地,Amazon EventBridge 规则每分钟触发一次金丝雀 Lambda 函数。
- 金丝雀向 API Gateway 端点建立 WebSocket 连接,发送心跳并发布自定义 Amazon CloudWatch 指标。
- CloudWatch 报警会评估该指标。如果金丝雀探测报告连续失败或停止发送数据,报警将触发,关联的 Route 53 健康检查会将主区域标记为不健康。
- Route 53 会检测到主区域不健康,开始将域名解析到次级区域。
- 客户端通过相同域名重新连接,现在能够访问次级区域,并从本地 DynamoDB 副本恢复应用程序状态。
先决条件
要跟随本文操作,您需要:
- 具有创建 Amazon API Gateway、AWS Lambda、Amazon DynamoDB、Amazon CloudWatch 和 Route 53 资源权限的 AWS 账户。
- 在 Amazon Route 53 中具有托管区域的域名。
- 在主区域和次级区域中,为您的域名配置的 AWS 证书管理器(ACM)证书。
- 已安装并配置 AWS 命令行界面(AWS CLI)v2。
- Node.js 18.x 或更高版本(用于客户端应用程序)。
部署解决方案
完整的源代码和部署说明可在 GitHub 仓库中找到。该仓库包含用于 WebSocket API、金丝雀健康检查以及包含本文所述重连逻辑的 React 客户端应用程序的 AWS CloudFormation 模板。
演示应用程序
该示例应用程序是一个实时拍卖应用,多个用户可以同时对同一物品出价。每次出价都会通过 WebSocket 消息更新共享计数器,已连接的客户端可以实时看到当前出价。该应用程序会显示响应由哪个 AWS 区域提供,使测试期间的故障转移过程可见。尽管这是一个简化示例,但这种模式代表了需要通过 WebSocket 连接共享可变状态的实时工作负载:聊天应用、协作编辑器、实时仪表板或多玩家游戏。
跨区域管理连接状态
WebSocket 连接是持久的,但在故障转移期间,构建在它们之上的应用程序状态才是关键。当用户与应用程序交互时,服务器会根据其身份构建会话数据。当发生故障转移且客户端重新连接到不同区域时,需要确保这些会话数据可用,以保持体验的连续性。此解决方案将状态存储在 DynamoDB 全局表中,该表会异步将数据复制到次级区域,通常在一秒内完成。由于这种复制是异步的,故障发生前立即执行的写入操作可能无法及时到达次级区域。对于大多数应用程序来说,这个时间窗口足够小,可以接受。
故障转移后,客户端重新连接并恢复其状态。在此演示中,客户端在重新连接后发送 getState 消息,从次级区域的 DynamoDB 副本中获取最新的应用程序状态。
生产环境的应用程序在重新连接时需要额外考虑两个因素。首先,客户端需要重新认证:WebSocket API 在 $connect 路由上进行认证,因此故障转移后,次级区域会通过 Lambda 认证器重新认证客户端。请在两个区域中部署该认证器,以便重新连接的客户端可以向任一区域进行认证。其次,为了恢复状态,需要从经过验证的令牌中识别用户,例如 sub 声明。应根据用户 ID 而不是连接 ID(每次重新连接都会变化)来存储状态。由于不同应用程序对会话状态的建模方式不同,请根据您的架构进行调整。
如果您的应用程序使用关系型数据库,需要设置类似的跨区域状态复制。例如,Amazon Aurora 全球数据库可在区域之间提供亚秒级复制延迟。请注意,Aurora 在故障转移期间需要将只读副本提升为主数据库,这通常会使恢复过程增加 1-2 分钟。此提升操作也可以基于 CloudWatch 警报操作实现自动化。
心跳机制
客户端和金丝雀(canary)均使用应用层心跳机制验证 WebSocket 连接状态。定期发送轻量级消息并等待确认响应。若未收到响应或响应无效,表示连接已中断。客户端据此触发重连,金丝雀则利用该机制驱动 Route 53 健康检查。该机制在 RFC 6455 中有描述,是 WebSocket 连接健康监控的标准方案。
本文中的客户端代码片段使用了 react-use-websocket,这是一个提供内置重连和心跳支持的 React 库。您可以在其他 WebSocket 客户端库或编程语言中实现相同模式。
heartbeat: {
message: JSON.stringify({ action: 'heartbeat' }),
returnMessage: 'pong',
timeout: 35000, // 35 秒内未收到消息则关闭连接
interval: 30000, // 每 30 秒发送一次心跳
}自动检测区域故障
当主区域不可用时,Route 53 需要检测故障并将流量重定向至备用区域。为此,解决方案创建了一个主动监控 WebSocket 终端的健康检查。部署在主区域的 Lambda 函数(称为金丝雀)负责执行此健康检查。该函数每分钟通过建立 WebSocket 连接、发送心跳消息并验证是否收到有效响应来探测终端。金丝雀将结果作为自定义 CloudWatch 指标(App/WebSocketHealth,维度 ApiId)发布:1 表示健康,0 表示不健康。
CloudWatch 警报监控该指标,并将故障和缺失数据均视为不健康(TreatMissingData: breaching)。连续两次不健康数据点后,警报触发,关联的 Route 53 健康检查(配置为 InsufficientDataHealthStatus: Unhealthy)将主区域标记为宕机。Route 53 随即切换 DNS 至备用区域。
回退机制通过相同机制实现。当主区域恢复后,金丝雀开始恢复正常,警报解除,Route 53 将流量切换回主区域。对于生产环境工作负载,建议使用非对称阈值:设置较低阈值(两个数据点)实现快速故障转移,设置较高阈值(超过五个)实现较慢回退,这有助于防止主区域在恢复期间不稳定时出现抖动。60-120 秒的 DNS 生存时间(TTL)可在快速传播与查询效率之间取得良好平衡。
当连接中断时,客户端需要在不压垮正在恢复的服务的情况下重新连接。前面描述的心跳机制在大多数情况下可以处理检测工作。如果在超时窗口内未收到任何消息,库会关闭连接。然而,后端可能只是降级而非完全不可达。例如,当Lambda函数被限流时,API Gateway会返回错误响应,此时连接在技术上仍然保持打开,但应用程序无法正常工作。为应对这种情况,客户端会跟踪连续失败的心跳次数:即未收到预期的pong消息的响应。连续失败两次后,客户端会强制关闭连接并触发重新连接。以30秒的心跳间隔为例,这可以在60秒内检测到降级的后端。
客户端识别到连接中断后,会使用指数退避和全抖动(exponential backoff with full jitter)模式重新连接,该模式在AWS架构博客文章《Exponential Backoff And Jitter》中有详细描述。每次重试的间隔时间会加倍,而全抖动会在该时间窗口内随机化实际延迟。这种机制可以将重连尝试分散到不同时间点,有助于防止大量客户端同时重试导致的“雷击效应”(thundering herd),从而避免DNS切换瞬间压垮备用区域。
reconnectInterval: (attempt) => {
// 指数退避与全抖动
const expDelay = Math.min(2000 * Math.pow(2, attempt), 20000);
return Math.random() * expDelay;
}每次成功重连后,客户端会向服务器请求当前的应用状态。由于客户端重新连接到相同的自定义域名,Route 53会将其解析到当前健康的区域。客户端无需知道正在连接的区域。服务器会在响应中包含区域信息,应用程序可根据此信息更新UI或日志。
故障转移的实际演示
要查看完整的故障转移流程,请启动客户端应用程序并连接到WebSocket端点:
cd client
cp .env.example .env.local
# 在.env.local中编辑自定义域名URL
npm install
npm run dev在浏览器中打开应用程序并注意UI中显示的当前服务区域。
图2:客户端连接到主区域(us-east-1)
接下来,通过将主Lambda函数的保留并发数设置为零来模拟故障,这将阻止其处理请求:
aws lambda put-function-concurrency \
--function-name counter-handler-us-east-1 \
--reserved-concurrent-executions 0 \
--region us-east-1大约两分钟后,金丝雀探测会在一分钟间隔内连续两次失败后检测到故障。Route 53随后将主区域标记为不健康,DNS开始解析到备用区域。
客户端检测到连接中断后,开始使用指数退避和抖动进行重试。
图3:客户端检测到故障并使用指数退避重新连接
当DNS传播完成后,下一次重连尝试将到达备用区域。客户端向服务器请求当前状态,从DynamoDB全局表中恢复数据,UI更新以显示新的服务区域。
图4:故障转移至备用区域(us-west-2)后的客户端,状态已保留
当主区域恢复后,金丝雀检查会确认其已恢复正常。警报解除后,Route 53 会通过相同的自动流程将 DNS 切换回主区域,客户端重新连接到主区域。故障转移过程中无需人工干预。
清理
为避免持续产生费用,请删除本操作指南中创建的资源:
- 删除 CloudFormation 堆栈:aws cloudformation delete-stack --stack-name websocket-health-check --region us-east-1 aws cloudformation delete-stack --stack-name websocket-counter-secondary --region us-west-2 aws cloudformation delete-stack --stack-name websocket-counter-primary --region us-east-1
- 从控制台手动删除 Route 53 故障转移记录和 API Gateway 自定义域名。
结论
在本文中,您学习了如何为 API Gateway WebSocket API 构建多区域灾难恢复模式。通过将应用状态与连接本身分离,您可以在区域故障转移后自动恢复会话。此模式适用于 WebSocket 连续性至关重要的实时应用。根据您的工作负载调整健康检查阈值、重连时机和状态恢复方式。
如需了解更多关于本解决方案中使用服务的信息,请参考以下资源:
- Amazon API Gateway WebSocket APIs
- Amazon DynamoDB Global Tables
- 为 DNS 故障转移配置自定义健康检查
- 指数退避与抖动算法
作者简介
' "