1. 设置超时
在应用中设置服务调用的超时时间后,一旦线程的执行时间超过了所设置的时间,就抛出异常信息,自动断开连接,这样服务的线程就不会都长时间僵死在调用异常的服务上,导致没有空闲线程接收新的用户请求,可以避免Service A因为调用Server E 异常而被拖垮,自身不可用。所以通过网络调用外部依赖服务时,都必须设置超时。
2. 使用断路器
断路器大家都不陌生,家里电表在电流过载或者短路时就会跳闸,如果不跳闸,电路就不断开,电线就会升温,造成火灾。有了断路器之后,电流过载时就会自动跳闸断开电路,避免引起更大的灾难。
在程序中也是如此,当知道服务调用某个依赖服务有大量超时的时候,再让新的请求去访问也只会超时,并不能得到想到的结果,还会消耗现有资源,增加负载,导致服务不可用。
这个时候使用断路器就能避免这种资源浪费,在自身服务和依赖服务之间放一个断路器,通过断路器的监控实时统计访问的状态,当访问超时或者失败达到某个阈值的时候(如50%请求超时,或者连续20次请失败),就打开断路器,那么后续的请求就直接返回失败,而不是一个长时间的等待,再根据一个时间间隔(如30秒)或请求超时的情况(如0%的超时)尝试关闭断路器(或者更换保险丝),看依赖是否恢复服务了。
一个服务依赖多个服务时,如果其中一个非核心的依赖不可用,通过设置超时和使用断路器,可以确保Service A在调用异常的Service E并不会导致自身的异常,在大部分情况下服务还能健康运转,可以很好的做到依赖隔离。如下图所示:
4)设置限流
在服务访问的高峰期可能因为大量的并发导致性能下降,严重时将会有大量的请求排队,可能会导致服务宕机。为了保证应用的可用性,可拒绝低优先级的调用,让高优先级的请求成功,避免所有调用都失败的情况,并且为每个依赖服务提供一个小的线程池,如果线程池已满调用将被立即拒绝,默认不采用排队,可以加速失败判定时间。这样的结果是有些用户可以访问,而有些用户失败,但失败的用户重新访问又可以是正常的访问。这样能确保服务的可用性,而不是完全不可用。
虽然有了上面的一些可提高系统可用性的措施,但系统是复杂的,一个简单的修复都有可能造成不可想像的后果,且系统又是动态的,有些系统可能一天都发布几次,几十次。在这种情况下 故障依然是不可避免的。比起半夜深睡或正在享受节假日的美好时光时系统故障来当救火队员,会做更多的措施来提高系统的可用性。比如在某些企业里会定期举行生产环境的故障应急演练。
过去都是在业务低峰时进行人为故障测试高可用方案是否生效,包括主机,网络,应用,存储等每一层架构都进行演练,而现在也逐渐的在正常的生产时间进行故障应急演练,检查系统的高可用性。问题在于可能在演练时能够立即恢复,但真实故障发生时还是会出现长时间故障得不到恢复的情况。一个是演练是按照已知的场景制定的方案实施,二是演练的范围基本是高可用节点的切换或灾备系统的切换,第三个问题是这个演练是人为操作,需要全员的参与,并不会频繁的举行。但系统是动态的,这次是高可用的,不代表下周,或下个月还是高可用的。
当单体架构变成微服务架构后,应用层的演练就会变得复杂,就像前面提到的,如果每个服务只有一个故障可能都会有 2 100 种不同。因此需要有一种自动的故障测试方式来回避微服务化后演练实施的可操作性。
Netflix公司提出了一种自动故障测试的方案来提高微服务架构的可用性。这个测试方案也是在生产环境中进行,而故障测试的最终目的,是为了当真的有故障发生时,生产环境不会停止服务,并且整套系统可以在没有人为干预的情况下,非常优雅地通过降级将发生故障的部分组件排除出去。他们认为如果只在测试环境中测试,而真实生产环境的业务压力,业务场景,环境配置、网络性能和硬件性能都没有测试过,当故障在生产环境中真实发生时发现缓解问题的方案可能会失效。而且这个测试只在工作时间运行,这样工程师可以得到告警并及时响应。