微服务带来的不安因素与单体架构相比,微服务架构下的服务调用从同一台机器的本地调用变成了不同机器之间的远程调用。这也带来了以下不确定因素:调用的执行者是服务提供者。即使服务消费者本身是正常的,服务提供者也有可能因为CPU、网络I/O、磁盘、内存、网卡等各种原因调用失败。可能是因为GC挂起等程序执行问题导致两台机器之间出现调用失败,所以必须通过网络传输,网络不可控,丢包,延迟或者抖动都可能导致调用失败.因此,需要对服务调用失败进行特殊处理。超时微服务下的一次用户调用可能会拆分成多个系统之间的服务调用,任何一个服务调用出现问题都可能导致用户调用最终失败。系统出现问题,会影响所有调用系统提供的服务的服务消费者,造成服务雪崩。因此,必须为服务调用设置超时时间,防止依赖的服务返回结果阻塞服务消费者。如果超时时间设置得太短,一些服务调用可能还没有及时执行就被丢弃了,这可能会导致服务消费者被拖死。99.9%或99.99%应根据服务商的真实在线服务水平来取。以呼叫返回的毫秒数为准。重试虽然可以通过设置超时及时止损,但是服务调用结果终究是失败了。在大多数情况下,呼叫失败是由于网络问题或个别服务提供商节点出现问题。如果能换个节点重新访问,可能会成功。如果一次服务调用的失败概率是1%,那么连续两次服务调用的失败概率就是0.01%,失败率降到原来的1%。因此,经常需要设置服务调用超时后的重试次数。如果一次服务调用的超时时间设置为100ms,重试次数设置为1,当服务调用超过100ms时,服务消费者会立即发起第二次服务调用,而不会等待第一次调用的结果返回.如果双发一次调用不成功的概率是1%,那么连续两次调用不成功的概率就是0.01%。提高服务调用成功率的一个简单方法是同时调用服务发起两个服务调用可以提高调用的成功率。两次服务调用,谁先返回就使用谁的返回结果,平均响应时间比一次调用快。这是双重发送。但是这样的调用会让后端服务的压力加倍,消耗的资源也会加倍,所以“鲁莽”的双发是不可取的。更智能的双发,即“BackupRequests”服务消费者发起一次服务调用后,如果在给定时间内没有返回请求结果,则服务消费者立即发起另一次服务调用。注意设置时间通常比超时时间短很多。比如超时时间是P999,那么备份请求时间可能是P99或者P90,因为如果在P99或者P90的时间内调用还没有返回结果,那么很大概率可以认为是这个请求是一个慢请求,再次发起调用理论上返回更快。实际在线服务运行时,由于尾请求时间长,P999可能比P99和P90大很多。比如一个服务的P999是1s,而P99只有200ms,P90只有50ms。在这种情况下,如果备份请求时间为P90,那么第二次请求的等待时间仅为50ms。备份请求应该设置一个最大重试率,避免当服务器端出现问题时,大部分请求的响应时间会超过P90,导致请求量几乎翻倍,给服务商带来更大的压力。可以设置为15%,在不给服务商增加太多额外压力的情况下,尽可能体现备份请求的优势。它不能提高服务调用的成功率,而且还会加剧失败,因为重试会给服务提供者带来更大的压力。服务消费者需要能够检测到服务提供者的故障,在短时间内停止请求,给服务提供者从故障中恢复的时间,并在服务提供者恢复后继续请求。原理将客户端的每一次服务调用都封装了一个断路器,通过断路器对每一次服务调用进行监听。如果在一定时间内服务调用失败的次数达到一定的阈值,就会触发熔断器,后续的服务调用将直接返回,不再向服务提供者发出任何请求。熔断器断掉后,一旦服务提供者恢复服务调用,如何恢复Hystrix断路器包含三种状态:关闭状态、打开状态和半打开状态。正常情况下,断路器处于关闭状态,偶尔的调用失败不会影响处于打开状态的服务。当调用失败次数达到阈值时,断路器将处于打开状态,后续服务调用将直接返回,不向服务提供者发起请求。半开状态断路器合闸后,每隔一段时间就会进入半开状态。向服务提供商发起探测调用,以确定服务提供商是否已恢复正常。如果调用成功,熔断器将关闭;如果失败,断路器将保持打开状态,等待下一个周期重新进入半开状态。Hystrix会将每一次服务调用封装成HystrixCommand,并实时记录每一次服务调用的状态,包括成功、失败、超时或被线程拒绝。当某个服务调用的失败率在一段时间内高于阈值时,Hystrix的断路器会进入打开状态,新的服务调用会直接返回,不会向服务提供者发起调用。等待设定的时间间隔后,Hystrix的断路器会再次进入半打,新的服务调用可以重新发送给服务提供者。如果一段时间内服务调用失败率仍然高于阈值,断路器将重新进入打开状态,否则将重置为关闭状态。判断断路器是否断开。故障率阈值通过以下参数传递:HystrixCommandProperties.circuitBreakerErrorThresholdPercentage()断路器进入半开状态的时间间隔通过以下参数传递:HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds()断路器的关键在于计数一段时间内服务调用的失败率滑动Window算法默认滑动窗口包含10个桶,每个桶的时间宽度为1s,每个桶记录成功、失败、超时、拒绝的次数在这1秒内的所有服务调用。当新的1到来时,滑动窗口会向前滑动,丢弃最旧的bucket,并包含最新的bucket。在任何时候,Hystrix都会以滑动窗口中所有服务调用的失败率作为判断断路器开关状态的依据。将这10个桶中记录的所有失败、超时、线程拒绝调用的总和除以总调用次数即为滑动窗口内所有服务调用的失败率。总结对于非幂等,即同一个服务调用多次重复返回不同的结果,不能重试。例如,大多数上行链路请求是非幂等的。双发在重试的基础上做了优化,减少超时等待时间,对于长尾请求场景非常有效。采用双发后,可以大大降低服务调用的P999,是提高服务调用成功率的有效手段。Fuse可以很好的解决依赖服务失效带来的连锁反应。对于线上大规模的服务调用,尤其是非关键路径的调用,即使调用失败,对最终结果的影响也很小。应引入保险丝。重点参考https://martinfowler.com/bliki/CircuitBreaker.htmlhttps://github.com/Netflix/Hystrix/wiki/How-To-Use。转载本文请联系JavaEdge公众号。
