非常著名的设计模式, 用来处理服务不可用的状态下的情况. 尤其在流处理系统中, 避免某个点的错误无限向后扩展. 让错误在系统内被及时终止是很有必要的.
问题
在一个云处理平台上, 很多操纵/算子是经过网络在远程集群上处理并返回结果的. 网络抖动, 物理机崩溃, 自然灾害等可能让业务层的机器永远无法等到结果.
首先会重试数次, 但重试失效后是否需要降级处理, 以及通知其它服务故障状况成为一个决策.
一个坏掉的biz节点可能导致大量的上端业务停摆, 并持续重试, 交换机可能被重试链接淹没. 网络延迟瞬间增大, 从而导致更多的服务超时, 更多的重试链接被建立, 整个业务链最终崩溃!
解决
熔断机制致力于解决在服务失效时的重试问题, 以及服务失效后的影响范围控制问题. 在这种设计模式下, 业务端能够获知它依赖的服务的状态, 从而决策重试的次数, 以及决策是否使用降级后的解决方案来解决问题
熔断机制非常类似于一个proxy, 它在转发请求的同时, 需要检测请求失败的次数, 如果失败次数过多. 它可以根据策略, 通知上层应用停止发送服务请求, 并对所有等待中的请求立即返回错误.
更进一步的, 它可以启动备案状态, 将关键请求转发到备份的机器上从而保证一些核心业务以比较高的代价可以持续运行. 熔断开关可以表现成一个状态机
ebay公司在搜索业务中如果cache层, elasticsearch层都大面积失效. 会去底层数据库进行一个非常重的扫描以重建关键索引
正常 转发业务, 并对各种失效进行统计. 根据统计结果来执行不同的策略. 如因为网络超时造成的反复失败可能是因为交换机崩溃, 应该立即熔断相关服务. 如因cache不命中导致的链接越来越多, 超时不断发生. 应立即启动降级机制, 保证核心功能运行, 同时停止其它服务避免整个网络彻底崩溃
熔断 对于所有的请求立即返回错误, 并通知它们不需要重试
降级 对于关键核心业务, 以非常大代价的方式来运行. 对于一般业务, 直接返回错误, 并通知它们不需要重试
在具体实现中, 统计部分可能需要按照一个滑动窗口来进行统计.
决策
哪种错误是适用于这种策略 并不是所有的错误都适用于这种模式, 从经验上来看, 熔断模式主要应用在OLTP中, 并且实践中倾向于应对因为网络问题带来的错误
日志 熔断模式并不能解决问题, 在熔断器上实现良好的日志有助于帮助开发人员发现问题并且根治它. 但过于复杂的日志显然对一个proxy非常不友好, 日志的详细程度就成了一个权衡点
自动恢复 在降级/熔断模式下如何进行恢复, 像很多带有HA的数据库, 在切换时虽然不可以写但是可以读, 在stand-by切换成功后可正常读写. 底层服务提供者如何通知熔断器它恢复了是一个需要考虑的点
主动监测错误 一个比较薄的设计方式是让熔断器间歇性的主动探测后续服务的死活, 这样它就不用去监控它转发的每一条请求而且还要记日志. 这种设计非常的薄, 容易维护, 坏处是需要启动一个额外的监控进程
人工恢复 相对于自动恢复的设计, 人工恢复显然在代码上薄的多, 当DevOps们确定后续服务恢复了就重启熔断器就完事了. 坏处是很多流处理场景争分夺秒, 像计算广告业务连100ms都等不了, 怎么可能等人去重启
并发控制 熔断器和所有其他的proxy, 不管是硬件交换机还是虚拟网管NAT一样, 都面对高并发问题, Netty等等的底层设计模式了解一下
资源隔离 传统上每个熔断器处理且仅仅处理一种资源的访问, 但是对于那些紧耦合的项目, A访问同一个服务的B和C两个多态功能, 一个成功了, 一个失败了. 如何执行后续策略是一个非常有意思的问题, 实际上如果整个系统的耦合度这么高, 架构师可以考虑重写一下?
加速熔断 熔断器代理的服务主动要求熔断, 如HBase发现ZK数量不足以支持一致性了, 可以主动要求熔断并主动进入降级处理流程, 以节约后续的时间. 坏处就是逻辑因此变厚了.
错误分类 我们在前面也讨论过, 不同的错误会导向不同的熔断策略. 甚至不同的错误可能导向不同的降级策略
全局TTL设置不当 在一个大的分布式系统里, 往往到处都有timeout配置, 如果配置不当, 就会导致"假性错误", 在proxy两端的程序都还没超时, proxy自己熔断了. 或者反过来, proxy还在傻傻的等, 上游的请求却先断开了. 如何保证所有地方的TTL配置是合理的, 对DevOps以及熔断器的开发者都是挑战. 毕竟人一定会犯错.