微服务如何保证不会出现连锁反应?Go 实现的断路器了解下
本文作者:杨锡坤
原文链接:
本文学习参考自: Circuit Breaker pattern [1] 和 cep21/circuit [2]
业务问题场景在业务系统中 , 通常存在服务之间的相互调用 , 例如服务 A 调用服务 B , 当出现如下情形:
- 服务 A 与服务 B 之间的网络出现异常
- 服务 B 过载
- 服务 B 出现异常
断路器模式在家庭电路中有一个叫断路器的安全设备 , 当出现电路过载、短路、漏电等情况时 , 就会发生跳闸 , 防止出现安全事故 。 类比到上面描述的业务问题场景 , 我们需要在系统中实现一个类似断路器功能的组件 , 用于阻止系统 A 重复尝试很可能失败的调用 。
在断路器模式中 , 断路器组件需要监测到最近失败的调用 , 并且利用这些信息去决定新的调用是否执行 , 还是立即抛出异常 。 当断路器组件“跳闸”之后 , 还需要能探测被调用服务是否恢复正常 ,
断路器模式的代码实现 , 使用了有限状态机的思想 。 最基本的实现有三种状态:
- 关闭(Closed):调用正常执行 。 断路器组件对最近失败的调用进行计数 , 当达到阈值时 , 则断路器组件“跳闸” , 进入“打开”状态 。
- 打开(Open):调用请求会立即失败 , 断路器组件抛出异常 。
- 半打开(Half-Open):当处于“打开”状态时 , 会启动一个超时定时器 , 当超时后 , 断路器组件会进入“半打开”状态 , 此时允许执行部分调用 , 断路器会对成功执行的调用进行计数 , 达到阈值后 , 会认为被调用服务恢复正常 , 断路器状态回到“关闭”状态 , 如果有请求出现失败 , 则回到“打开”状态 。
文章插图
问题和注意事项
- 异常处理:系统需要考虑到断路器抛出的各类异常该如何处理 。 比如采取降级措施 , 把请求转发给备份服务 , 或者通知上游稍后重试 。
- 异常的类型:服务调用请求可能出现超时 , 或网络不通 , 下游服务明确返回失败的情况 , 断路器可能需要针对不同情况的错误 , 采取不同的状态切换策略 。 例如触发切换到“打开”状态的条件 , 可以是超时错误的阈值比下游服务明确返回失败的阈值更高 。
- 日志:断路器需要记录下所有失败的请求 , 方便相关人员监控定位问题 。
- 恢复:配置合适的策略 , 让断路器检测下游服务是否恢复正常 ,
- “打开”到“半打开”的状态切换:可以不使用定时器 , 而是周期性的探测下游服务是否恢复 。
- 人为干预:服务异常恢复需要的时间有长有短 , 断路器最好能提供人为控制的接口 , 方便将断路器强制切换到“打开”或“关闭”状态 。
- 并发:一个断路器可能会被很多请求并发访问 , 所以断路器工程化实现所需的时间和空间消耗需要尽量的小 。
- 资源差异:为不同的资源访问 , 单独创建相应的断路器 。
- 加速“跳闸”:当可以从下游服务获取到足够明确的异常时 , 则立即切换到“打开”状态 。
cep21/circuit 中主要的类型和接口:
circuit.Manager
// 管理多个circuits对象实例type Manager struct {// func (h *Manager) CreateCircuit(name string, configs ...Config) (*Circuit, error) 方法创建circuits对象实例时 , 使用的配置 , 会按照逆序将多个配置合并为最终的配置DefaultCircuitProperties []CommandPropertiesConstructor// 每个circuits会有一个唯一命名的标识circuitMap map[string]*Circuit// 用于circuitMap的读写锁mu sync.RWMutex}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 亚马逊终止托管服务:Parler网站下线
- 大一非计算机专业的学生,如何利用寒假自学C语言
- Git服务器配置错误导致日产汽车源码在网上泄露
- 虾米音乐,下个月正式停止服务
- 虾米音乐播放器将于2月5日停止服务,今开启用户资产处理通道
- 服务|虾米音乐:2月5日关停3月5日后将无法登录
- 天猫精灵App全新升级,推出“精灵家”服务
- 快递员拒绝送货上门并大喊大叫!经济学者马光远吐槽德邦快递服务烂:流氓至此,坚决抵制
- 亚马逊宣布停止为Parler提供托管服务
- 亚马逊员工权益组织呼吁AWS拒绝为Parler提供托管服务