一. 背景
导致系统服务不可用的有诸多因素,大多数可以由外到内归结为以下几种:
1、高并发流量;
2、系统所依赖的第三方服务(数据库、缓存、接口等);
3、系统软件中的bug和运行过程中的不稳定因素;
4、运行系统的硬件故障。
对于硬件故障导致的系统不可用,可以在负载均衡器中将该节点摘除,使流量都能在剩余节点中得到正常处理。本文中主要探讨在应用系统之中实现上述前三种情况的判定以及降级方式。
二. 高并发流量
对于一个应用系统来说一定会有极限请求数,如果超了该阀值则系统就会不响应用户请求或响应的非常慢。以tomcat为例,有最大连接数参数maxConnections,超出之后会拒绝连接。对于实际需求来说,往往需要根据不同的业务接口请求采用多种不同的判断超过阈值并限流的方式,并且对超出阈值的流量进行降级处理,使服务保持可用。
例如:设定阈值为某一个接口同时能够处理的最大请求数,这时可以使用Java中的AtomicLong进行当前请求数的记录并对本次请求进行判断,超出该数目即可对本次请求进行降级逻辑处理。
=================================
try { if(atomic.incrementAndGet() > 最大请求数) { //降级处理逻辑 } //处理请求 } finally { atomic.decrementAndGet(); }
=================================
此外还可使用令牌桶算法和漏桶算法进行限流,其中的某些实现可以允许不同程度的突发流量、整形为平滑流量或控制时间窗口内的流量,以应对不同的业务场景。
降级处理逻辑可以有以下几种形式:
1、使用兜底数据或默认数据;
2、选取较为重要的请求参数对正常处理请求的结果进行缓存,降级时读取缓存数据;
3、使用一个相对于正常请求处理逻辑耗时较少的简化处理逻辑;
其目的是要保证用户仍然能获取到有损服务而不是没有服务,同时降级逻辑首先保证的是快速响应并占用较少的系统资源(主要表现在cpu以及io),保证整个服务仍然可用,在此基础上尽可能的提高降级处理逻辑的服务质量。
三. 第三方服务
第三方服务的可用性可以通过以下两个方面来判断:
1、 服务响应时间对系统可用性造成的影响;
2、 服务返回结果的可用性;
根据系统本身的响应时间、qps以及各模块的处理时间,来估算出该服务响应时间的阈值。调用服务超时后进行截断并判断本次服务不可用,进行降级处理。在后续使用中不断对超时时间进行修正,在不影响系统性能和响应时间的前提下,保证尽可能多的请求不被超时截断。
正常接收到响应后,可以利用现有解析响应结果的逻辑来进行格式正确性的校验。解析出现异常情况时判断本次服务不可用并进行降级处理。在后续使用中根据实际需求可以对数据内容加一部分正确性校验。
图1 第三方服务可用状态判断流程示意图
在实际使用中,第三方服务的不可用往往会持续一段时间,在这种状态下每次都调用服务并进行可用性判断会影响系统本身的性能,尤其是在第三方服务大量超时的情况下。因此可以采用图1中的服务可用状态判断流程。要点如下:
1、“服务调用”中,包含了上文中所述的单次请求的可用性判断以及不可用时的降级处理。同时进行该服务调用次数和异常数的记录。
2、“更新服务可用状态”中,为了减少个别请求异常引起的误判以及系统性能损耗,服务可用状态检查在满足一定条件下才会被触发,例如:与上一次检查时间点的间隔以及最少调用次数的双重限制。满足条件后根据异常率(异常数/调用次数)判断和更新服务可用状态,并将调用数、异常数清0,同时记录该时间点以备下一次检查使用。检查间隔、最少调用次数、判定不可用的最小异常率等都可设为参数并根据后续使用情况不断调整。
3、“本次请求是否作为探测”中,当服务状态不可用时,仍然保留一部分流量进行探测,以便在服务正常后,能够自动且快速的恢复成可用状态。为了达成这个目的,需要设定适当的流量比例,对服务状态不可用时的请求进行分流。
降级时处理逻辑可以有以下几种形式:
1、对于必需的服务,可以构造默认数据并在降级时使用;
2、对于非必需服务,即不会影响系统功能和正常使用,只会影响系统效果时,可以在系统中进行服务不可用时的兼容处理;
3、对于服务数据并非实时处理的需要,而是定时更新的情况,可以选择不进行更新,继续使用先前获取的有效数据。
四. 系统自身bug及不稳定因素
系统本身可能会包含很多不稳定因素,在某些处理逻辑或长时间运行的情况下会导致服务不可用,例如:内存泄漏、死循环、线程阻塞、线程定时创建但没有回收等。
针对以上情况,判断降级相对前两种情况相对复杂,同时在系统自身bug导致异常时,系统中的降级逻辑可能不会正常工作。因此建议降级的粒度细化到核心的业务类和方法,同时实行手动降级和自动降级相结合的方式。
自动降级:常用的判断参数有线程数、处理时间、结果有效性等。同时可以使用单独的应用心跳检查系统接口的可用性,并通过系统特定接口设置是否降级。
手动降级:在人工发现系统不可用后,手动进行切换。在应用层,可以通过系统特定接口来切换;在接入层,可以通过nginx等代理服务器的配置来切换。
降级时处理逻辑可以有以下几种形式:
1、系统应用中使用兜底数据或默认数据,适用于自动降级;
2、在nginx等代理服务器中切换到默认页面,适用于自动降级不可用时的手动降级;
3、使用上一版本或稳定的逻辑,适用于新的不稳定的逻辑上线时。