前置知识
熔断在微服务架构里面是指当前微服务本身出现问题时,会拒绝新的请求,直到微服务恢复
熔断给了服务端恢复的机会,比如如果CPU使用率已经100%了,服务端因此触发了熔断,那么拒绝了新的请求之后,服务端的CPU使用率就会在一段时间内降到100%以内。
提炼出两个点进行讨论
- 怎么判断微服务出现了问题
- 怎么知道微服务恢复了
判断服务的健康状态
类似在负载均衡里讨论的动态算法,本质上也是要求自己的业务来选择一些指标,代表这个服务器的健康程度。比如说一般可以考虑使用响应时间、错误率。
比如我们把响应时间作为指标,那么响应时间超过多少应该触发熔断,是根据业务来决定的。比如如果业务对响应时间的要求在1秒以内,那么你的阈值就可以设定在1秒,或是稍高一点,留点容错的余地。
假如产品经理没跟你说这个业务对响应时间的要求,就可以根据整体响应时间设定一个阈值,原则上阈值应该明显超过正常响应时间。
要求响应时间超过一段时间之后才触发熔断,一个原因是响应时间可能偶发性地突然增长;另一个原因是防止抖动。
一段时间的长度很大程度上依赖个人经验,如果时间过短,可能会频繁触发熔断,然后又恢复,再熔断,再恢复……反过来,如果时间过长,那就可能导致该触发熔断的时候迟迟没有触发
可以根据经验设定一个值,比如三十秒或一分钟
当然最简单的做法就是超过阈值就直接触发熔断,但是采取这种策略就要更加小心抖动问题。
服务恢复正常
一个服务熔断之后要考虑恢复,比如说如果我们判断一个服务响应时间过长,进入了熔断状态。那么十分钟过后,已接收的请求都已经被处理完了,即服务恢复正常了,那么他就要退出熔断状态,继续接收新请求。
因此在触发熔断之后,就要考虑检测服务是否已经恢复正常。
大多数的微服务框架都是触发熔断之后保持一段时间,比如说一分钟,一分钟之后就认为服务已经恢复正常,继续处理新请求。
抖动就是服务频繁地在正常 - 熔断两个状态之间切换。
引起抖动的原因是多样的,比如前面提到的一旦超过阈值就进入熔断状态,或者我们这里说的恢复策略不当也会引起抖动。再比如刚刚提到的“一分钟后就认为服务已经恢复正常,继续处理新请求”就容易引发抖动问题。
如果熔断本身是高并发引起的,在一分钟后,并发依旧很高,这时候一旦直接恢复正常,然后高并发的流量打过来,服务是不是又会触发熔断
而要解决这个抖动问题,就需要在恢复之后控制住流量,比如按照10%、20%、30%……逐步递增,而不是立刻恢复100%的流量
显然这种做法还是不够好,因为在这种逐步放开流量的措施下,依旧有请求因为熔断不会被处理。那么一个自然的想法就是,能不能让客户端来控制这个流量?简单来说就是服务端触发熔断之后,客户端就直接不再请求这个节点了,而是换一个节点。等到恢复了之后,客户端再逐步对这个节点放开流量
面试准备
- 怎么判断微服务出现故障的?比如错误率、响应时间等
- 怎么判断微服务已经从故障中恢复过来的
- 在判断微服务已经恢复过来之后,有没有采取什么措施来防止抖动的问题
关于熔断最佳的面试策略是把它作为你构建一个高可用微服务架构的一环。例如你在介绍某一个微服务项目的时候可以这样说:
这是一个高可用的微服务项目,为了保证它的可用性,采用了限流、降级、熔断等措施。
此外,如果面试官问到服务治理以及提高系统可用性的方法之类的问题,也可以用熔断来问答。如果问到了限流或降级,可以尝试把话题引到熔断上。如果问某个服务崩溃了怎么办,相当于是问怎么提高可用性防止服务崩溃,以及服务真崩溃了也要有措施防止拖累别的服务,熔断就是一个可用的手段
基本思路
假设准备用响应时间来作为指标,可以这么回答,关键词是持续超过阈值
为了保障微服务的可用性,在核心服务里接入了熔断。针对不同的服务,设计了不同的微服务熔断策略。
比如最简单的熔断就是根据响应时间进行,当响应时间超过阈值一段时间之后就会触发熔断。我一般会根据业务情况来选择这个阈值,例如产品要求响应时间是1秒,就会把阈值设定在1.2秒。如果响应时间超过1.2秒,并且持续三十秒,就会触发熔断。在触发熔断的情况下,新请求会被拒绝,而已有的请求会被继续处理,直到服务恢复正常。
- 阈值怎么确定?根据观测到的响应时间数据来决定
- 持续三十秒是如何计算出来的?基于个人经验,解释一下过长或过短的弊端
- 为什么多了0.2秒?留了余地,防止偶发性的响应时间变长的情况
- 怎么判断服务已经恢复正常了?可以回答等到一段固定的时间,然后尝试逐渐放开流量
微创新的方案,关键词是缓存崩溃。
一个接口并发很高,对缓存依赖也很严重,所以熔断策略就是如果缓存不可用,比如Redis崩溃了,就会触发熔断。因为如果不熔断的话,请求会因为Redis崩溃全部落在MySQL上,基本上会压垮MySQL。
在触发熔断后,会额外开辟一个协程,持续不断地ping Redis
这里用 Redis 来作为例子,可以将 Redis 替换为 MemCache 之类的,甚至你还可以将缓存替换成你业务上任何一个关键的第三方依赖。
这里还留了一些可以引导的点
- 缓存问题:在这里提到了Redis失效,这种情况类似于缓存雪崩,很自然地就可以把话题引导到如何处理缓存击穿、穿透、雪崩这些经典问题上
- 高可用MySQL:使用熔断来保护MySQL,类似地,也可以考虑用限流来保护MySQL
最后我提到了退出熔断状态,如果面试官了解抖动问题,那么他就肯定会追问“你是一次性放开全部流量吗?”,那么你就可以阐述抖动的问题,然后总结一下。
我这种逐步放开流量的方案其实还是有缺陷的,还有一些更加高级的做法,但是需要负载均衡来配合。
亮点方案
综合了负载均衡算法和熔断措施地方案
放开流量是在服务端处理的,也就是服务端还是收到了100%的流量,只不过有部分流量会被放过去并且被正常处理。
一个自然的想法是为什么不直接让客户端来控制这个流量
这里进一步结合在负载均衡里提到的根据调用结果来调整负载均衡策略的讨论,可以让客户端也采用这种负载均衡策略。
整体流程:
- 服务端在触发熔断的时候,会返回一个代表熔断的错误
- 客户端收到这个错误之后,会把这个服务端节点暂时挪出可用节点列表。后续所有熔断请求都不会打到这个节点上了
- 客户端等待一段时间后,逐步放开流量
- 如果服务端正常处理了新来的请求,就让客户端加大流量
- 如果服务端再次返回了熔断响应,客户端就会再一次把这个节点挪出可用列表
- 如此循环,直到完全恢复
可以这么回答,关键词是负载均衡
整体思路是利用负载均衡来控制流量。如果一个服务端节点触发了熔断,那么客户端在做负载均衡的时候就可以将这个节点挪出可用列表,后续请求会发给别的节点。在经过一段时间之后,客户端可以尝试发请求给该节点。如果该节点正确处理了,那客户端就可以加大流量。否则客户端就要再一次等待一段时间。
万一所有可用节点都触发熔断了,应该怎么办?
这个方案是需要兜底的,比如说如果因为某些原因数据库出问题,导致某个服务所有的节点都触发了熔断,那么客户端就完全没有可用节点了。不过这个问题本身熔断解决不了,负载均衡也解决不了,只能通过监控告警之后人手工介入处理了。