熔断模式与错误/延迟容忍系统

由来

现有工程涉及大量第三方供应商的调用,一次远程调用就像一个“黑盒”充满了未知因素 : 可能会失败,可能会挂起直到请求client的最大容忍时间,可能使请求路由到意外的代码…
第三方的各种"不靠谱"可能会消耗尽服务器的资源,从而阶梯式地导致一系列连锁"灾难"-整个Web线程资源耗尽,请求的堆积,上游的阻塞等等(参考Netflix文档)

以调用第三方接口进行人脸识别为例, 尽管市面上提供了大量的供应商选项(阿里,腾讯,Face++,LinkFace…etc). 如果搭建的系统没有熔断机制, 在代码层面上通过硬编码为 单个用户请求固定单个供应商, 整个系统的最高稳定性将会基于第三方依赖的最低稳定性,一旦依赖方出现了波动…无可避免的会导致整个系统的波动.

即使上述系统进一步优化,通过各种支持热更新的配置项做到"在不同环境下为单个用户请求指定不同供应商(比如官网注册的用户统一到供应商A而第三方注册的用户统一到供应商B,并且供应商A与B可以通过配置项变更), 该系统依旧依赖了大量的人力工作: 需要有人时刻监控供应商状态并且进行手动切换, 这在软件开发中可能是一件极度糟糕的事情…

基于上述场景, 为一个频繁进行远程服务调用的工程加入熔断相关组件可能是一件不错的事情

注:本文仅包含作者本文理解,各位大佬有任何不同见解欢迎留言指导

基本概念

熔断模式的基本概念非常简单 : 以面向对象语言JAVA为例, 请求第三方资源(RPC, HTTP请求, 分布式缓存读取,数据库的读写等等)相关的代码往往封装在一类对象中,只要将这些特定的类对象通过装饰的方式封装进CircuitBreaker对象中, 由CircuitBreaker对象进行追踪与监控调用状态(参考CircuitBreaker Pattern). 一旦失败/超时请求达到指定Threshold, 闭路器将会打开,后续所有路由到该对象的请求将会被快速拒绝(也可能会是静默失败,自定义路由等…将在本文后续探讨). 原先请求第三方资源的请求将不再会到达, 直到闭路器再次关闭.
CircuitBreaker

上图是本人基于熔断模式理解画的流程图, 图中左边相关的判断是在第三方调用发生闭路器打开/超时/失败 发生的, 其中是否"快速失败",“静默失败”,"自定义失败返回"的判断逻辑不分先后顺序(流程图结构限制),在开发场景往往是选择上述场景下的任何一种失败返回策略.

快速失败

在熔断器打开(或者请求失败/超时)情况下快速返回一个异常,中断请求的同时快速释放web容器线程资源

静默失败

在熔断器打开(或者请求失败/超时)情况下返回一个null,如果请求的资源数据是个可选项,用户请求会正常返回但是缺失该可选数据

静态失败返回(或者叫优雅降级)

在熔断器打开(或者请求失败/超时)情况下通过静态代码的方式(比如硬编码生成一个你认为"永远"不会出错的Response对象)返回一个响应, 用户可能无法获取到定制化(通过远程调用获取的数据)但是依旧获得一个统一的返回

定制化失败返回

很多场景下是给工程嵌入熔断模式的“重中之重”, 在熔断器打开(或者请求失败/超时)情况下通过自定义的方式给用户返回定制化的数据. 本人设计的熔断组件就选择了该策略模式,该模式会在本文后续进行进一步描述

为一个频繁远程服务调用的工程设计熔断组件

由于不便透露公司工程, 我以一个高度依赖第三方(阿里,腾讯,Face++,LinkFace…etc)资源的刷脸服务作为案例描述,如果有描述不具体的地方烦请谅解

需求分析

背景: 原有系统采用配置项的方式为不同环境下的用户配置不同的供应商(比如官网注册的用户统一到供应商A而第三方注册的用户统一到供应商B), 并且供应商A与B的请求共用同一个HttpClient进行资源请求, 共有同一个线程池, 在任意一家供应商挂掉的场景下会导致所有供应商调用故障, 需要引入熔断模式实现"不同供应商线程池级别的隔离, 单供应商挂掉后请求自动路由备选供应商"等需求.

技术选型:
延迟/错误容忍组件Hystrix

简单实现设计简单实现设计

上图是熔断组件设计结构图,为了方便理解, 本人将原有不必要的类与元素进行了大量删减

  • AbstractRequestHandler :所有请求的抽象处理器, 该处理器根据接口区分对应的业务类型, 为AbstractRequest抽象请求赋予remoteServerName(首选第三方资源)与availableServersList(当前可用第三方资源列表)
  • Director & CommandBuilder & RemoteCallCommand:分别对应建造者设计模式的指挥者, 具体建造者, 产品. 上图为了方便理解删除抽象层建造者. 通过AbstractRequestHandler传递AbstractRequest到指挥者来组装产品(RemoteCallCommand)
  • Router: 失败场景下的路由器,路由策略为 :
    1. 抽象层请求AbstractRequest携带参数remoteServerName(首选第三方资源)与availableServersList(当前可用第三方资源列表)
    2. 远程调用延迟/失败下从availableServersList.remove(remoteServerName)
    3. 在fallback(远程调用延迟/失败)场景下获取availableServersList第一个元素(配置首项)为AbstractRequest设置remoteServerName(首选第三方资源),传递该请求至Director构建新的RemoteCallCommand
  • Receiver: 命令模式的接受者,定义统一接口,通过对象型适配者的方式复用老工程的第三方调用Client对象

本人会在后续博客释放对应代码Demo讲解

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值