0 大纲
- Lower the Timeouts, and Let the Service Fail Early
- Add Circuit Breakers
- Capacity Planning
- Add monitoring and alerting
- Implement Structured Logging
- Use Idempotency Keys
- Be Consistent with Reconciliation
- Incorporate Load Testing
- Get on top of incident management
- Organize Incident Retrospectives
1 降低超时时间,让服务尽早失败
默认超时时间为 60 秒。根据 Shopify 的经验,5 秒的读取超时时间和 1 秒的写入超时时间是不错的设置。
超时时间也可以在数据存储中设置。例如,MySQL 有 MAX_EXECUTION_TIME 优化提示,用于以毫秒为单位设置每个 SELECT 查询的超时时间。
Go 中的 http.Client 和 Node.JS 中的 http.request 等其他编程语言中的 HTTP 客户端根本没有默认超时时间!这意味着一个无响应的服务器可能会无限期地占用您的资源,并不必要地增加基础架构费用。
2 添加断路器
Shopify 开发了 Semian 来使用 Ruby 中的断路器来保护 Net::HTTP、MySQL、Redis 和 gRPC 服务。
通过在检测到服务已关闭时立即引发异常,他们通过不等待预期会发生的另一次超时来节省资源。
就像在家中或公寓中会发现的断路器一样,一旦断路器打开或触发,就没有什么可以通过。
3 容量规划
如果我们的队列中有 50 个请求到达,处理一个请求平均需要 100 ms,那吞吐量是每秒 500 个请求。
N 1 查询会增加请求的延迟并降低吞吐量。
代码语言:javascript复制capacity = throughput x latency
4 添加监控和告警
谷歌的站点可靠性工程(SRE)书中列出了一个面向用户的系统应该监控的四个黄金信号: 延迟、流量、错误和饱和度。
5 实现结构化日志记录
将日志存储在集中地方,并使它们易于搜索。
指标提供了系统行为的高级概述,而日志记录允许我们了解单个 Web 请求或后台作业内部发生的事情。
在分布式系统中,传递某种关联标识符很有用。一个假设的例子是当买家在结账时启动支付,关联_id 由我们的 Rails 控制器生成。
6 使用幂等键
确保支付或退款只发生一次,尽管偶尔会出现小故障。
请改用通用唯一词汇排序标识符 (ULID) 作为这些幂等键,而不是随机版本 4 UUID。
在 Shopify 的规模下,每一百万次不可靠的支付处理机会意味着它每天发生很多次。如果这是超时的支付 API 调用,他们希望重试请求,但要安全地进行重试。
7 与调节保持一致
在数据库中存储与 Shopify 的金融合作伙伴的调节中断。
通过调节,他们确保自己的记录与金融合作伙伴的记录一致。他们调节单个记录,如费用或退款,以及尚未支付给商户的当前余额等汇总记录。
8 结合负载测试
如果传入工作的数量足够大,他们的服务器甚至会耗尽内存来存储队列上的工作并崩溃。
Shopify 定期模拟大量抢购活动以获得基准测试结果。
9 掌握事件管理
事件通常从值班服务所有者收到页面开始,这可能是基于监视的自动警报,也可能是如果有人注意到问题,他们会手动发送。
每个事件通道都有 3 个角色:值班事件管理器(IMOC)、支持响应管理器(SRM)和服务所有者。
10 复盘
对于每个事件,Shopify 会提出 3 个问题:确切发生了什么?他们对系统有什么错误的假设?他们可以做些什么来防止这种情况发生?
一旦了解了这些,通常会分配几个行动项来实施保护措施,以防止同样的事情再次发生。