在微服务架构中,各个服务之间往往是级联在一起的,一个服务发生故障时可能会造成以下灾难:
所以需要在微服务不可用时切断故障服务与其他服务的通讯。
熔断是对服务提供者说的,由于某些原因(比如网络不通、服务挂了、请求处理不过来)造成服务提供者不能提供服务时,服务提供者就需要切断和服务调用者的连接,不然就造成资源浪费或者队列打满,从而导致链式反应。
降级是对服务调用者来说的,当A服务调用B服务不通时,A服务就切断与B服务的通讯,同时采取降级措施,比如重试、返回空值,生成相应错误信息等。
我们应该以什么方式来运行熔断呢?微服务还是第三方库?
如果独立出来以微服务方式运行熔断器的话,有以下两方面问题:
所有请求都要先通过熔断器服务,这样在网络IO上就是很大的开销,会造成网关整体性能的下降。
不利于熔断状态的判断,比如由于队列满造成服务调用失败,这时就应该开启熔断器,但是如果熔断器是以微服务方式运行,这种情况就不好做。
所以最理想的方式是以三方库或者叫插件的方式来运行。
如果把熔断器放在被调用服务侧的话会有两方面的问题:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FOvt5LIO-1598435903719)(/Users/zhanghh/Documents/doc/newbusiness/lessions/pictures/api-fusing-5.png)]
在依赖方处理请求前,我们有两种处理方式:
熔断器粒度是在微服务级别呢?还是API接口级别?
我们知道熔断是发生在微服务之间的,当A服务的依赖B服务发生故障时,我们应该把A服务和B服务切断。请大家思考下这种场景:
B服务的其中一个API接口发生故障,而其他接口能正常工作。这种场景下也要把A服务和B服务整个切断吗?
我们看看下图,对比下:
服务A依赖服务B,而服务B依赖服务C、服务D、服务E,/api3
这个接口需要调用服务E。
按照图中所示,如果服务E发生故障,那么熔断器会把服务A和服务B完全隔离,这可能会造成服务A的整个瘫痪,这不是我们想要的。在实际工作中,特定API接口发生故障的概率是比较高的,比如我们在单元测试中对某个接口的测试不充分,导致某个接口很容易报错。所以熔断粒度最好能细到API接口级别,这样的系统架构更健壮。所以最好的方案是把把熔断细粒度到API级别。如下图所示。
熔断器生成的数据主要有以下几个方面:
这是很典型的树形结构,可以采用Etcd、Zookeeper、Consul等分布式数据库。树形结构如图:
当熔断开启后,为了保证网关/集群整体的可用性,我们需要提供一种退路(fallback)机制,比如从缓存中取上一次的结果,或者返回空值等等。
此外如果有多种退路,可以设计多级退路机制,当第一种退路机制失败后,再尝试第二种退路机制,以此类推。
请大家思考下,熔断器应该有几种状态?开启
和关闭
这两种状态我们是知道的,还有没有其他状态?
在发现了某个微服务有故障后,我们开启了熔断器,过了一段时间,有故障的服务被修复了,那我们怎么让熔断器知道要关闭呢?
我们可以在熔断器开启后,每隔一段时间放行一个请求,如果请求调用成功,我们就关闭熔断器;如果调用失败,我们再关闭,循环往复下去。
根据上面的分析,我们可以得出熔断器应该有三个状态:关闭
、开启
、半开启
。熔断器默认是关闭状态,当触发熔断后状态变更为开启
,在等待到指定的时间,再放行一个请求检测服务是否正常,这期间熔断器会变为半开启
状态,熔断探测服务可用则继续变更为关闭
,关闭熔断器。