弹力设计即容错设计,包括容错能力(服务隔离,异步调用,请求幂等),可伸缩性(有无状态),一致性(补偿,重试),应对大流量的能力(熔断,降级)
来源:极客时间左耳听风专栏
系统可用性测量
Availability = MTTF/(MTTF+MTTR)
MTTF:mean time to failure,平均故障前的时间
MTTR:mean time to recovery,平均修复时间
系统可用性 % | 宕机时间/年 | 宕机时间/月 | 宕机时间/周 | 宕机时间/天 |
---|---|---|---|---|
90% | 36.5天 | 72小时 | 16.8小时 | 2.4小时 |
99% | 3.65天 | 7.20小时 | 1.68小时 | 14.4分 |
99.9% | 8.76小时 | 43.8分 | 10.1分钟 | 1.44分 |
99.99% | 52.56分 | 4.38分 | 1.01分钟 | 8.66秒 |
99.999% | 5.26分 | 25.9秒 | 6.05秒 | 0.87秒 |
隔离设计 bulkheads
- 按服务种类隔离.但如果有跨服务的请求,会增加调用次数,并且会引入分布式事务的需求.
两阶段提交? - 按用户隔离(多租户),可以有三种方案:
- 完全独立资源
- 服务共享,数据独立
- 共享服务,共享数据
异步通讯
同步调用如果被调用方有问题,调用方会跟着出问题
异步调用可以增加吞吐量,服务间的解耦更加彻底
三种异步通讯方式:
- 请求响应式:返回结果可能是’正在处理’之类.需要调用方去定时轮询结果或者给接收方提供回调,处理完毕后通过回调通知
- 接收方向发送方订阅事件?
- 通过Broker.对Broker提出了高可用高性能可横向扩展并且能够持久化数据的需求
异步通讯带来的问题:
- 业务流程不够直观,需要有可视化工具去呈现
- 事件可能会乱序
- 需要有总控方来管理业务状态的变更逻辑,来保证业务的整体一致性.
请求幂等
幂等解决超时之后状态未知的情况.有两种解决方法
- 下游提供查询接口,如果查到表明已成功执行
- 上游直接重试,下游自己保证多次执行不会产生副作用
幂等需要有一个唯一性的ID来标识同一个事件.推荐snowflake,41bit毫秒级时间+5bit数据中心+5bit机器id+12bit毫秒内序列号.可以在每次插入时通过该唯一性ID来判断是否已经执行过.
数据库中插入可以用如下语句
1 | insert into … values … on DUPLICATE KEY UPDATE... |
更新使用
1 | update table set status = "paid" where id = xxx and status = "unpaid"; |
因为POST不是幂等性的,表单设计中通过会有一个隐藏的token,token保存到数据表中,提交时需要判重防止重复提交
PRG模式:post提交表单,响应redirect,之后get刚才post的请求
有无状态
无状态便于运维和伸缩
补偿事务
需要有工作流引擎,每个步骤都有补偿机制,反向补偿流程
重试
场景:重试认为某个故障是暂时的,所以重试.如果错误是永久的,重试没有意义
例如如下场景:调用超时,繁忙,流控,维护中,资源不足等
没有权限或者非法访问等情形重试没有意义
策略:一般需要重试的场景下采用指数退避策略来重试,防止造成过大负载
要点:
- 后端服务幂等性
- 重试时间和次数依据场景而定
- 如果不是一个短暂的错误,需要使用熔断,快速失败,防止重试浪费资源
熔断
熔断状态机:
- 闭合(closed):在一定时间间隔之内统计失败的比率或者次数,超过之后需要切换到断开(open)状态.此时开启一个计时器,超时之后进入半开状态,给一个修正错误的机会
- 断开(open):断开状态直接返回错误或者返回一个缓存数据
- 半开(half-open):半开允许一定量的访问去访问服务,如果都成功切回闭合,否则重置计时器
设计要点:
- 根据返回错误类型确定是重试还是熔断
- 自动检测,不需要计时器,检测服务可用之后可以产品从open切换到closed
- 手动重置,可以手动切换状态
开源实现:Netflix Hystrix
限流
NGINX的limit_conn及limit_request,TCP的拥塞控制,基于响应时间确定滑动窗口是一种基于响应时间的动态限流方案
常见限流方法:计数器,队列(带权重的优先级队列),漏桶及令牌桶
令牌桶可以平时积攒令牌之后有爆发的流量
漏桶会以恒定的速率处理,漏桶可以理解为一个队列加一个恒时处理的处理器
参考go实现:https://gobyexample.com/rate-limiting
限流之后可以带一个限流的标记(例如http头),这样后端服务可以根据该标记进行服务降级
降级
- 强一致性到弱一致性:例如同步返回变为异步处理,返回”正在处理中…”;数据从缓存读取而不是从数据库
- 停止次要功能:例如搜索评论等功能
- 简化功能:例如只返回商品数据,不返回评论等.
功能降级需要梳理业务功能,确定哪些是must-have,哪些是nice-to-have.数据方面的降级需要前端配合,例如不返回评论时就不显示,并且有标记能够知悉是降级还是就是没有数据