微服务架构中熔断和降级是保证服务高可用的一项重要功能点,微服务区别于一体化项目的最大区别也再于熔断和降级,很多微服务项目的开发人员对熔断的理解就是当服务不可用的时候,为了让整体服务可以正常运行,需要让后续的请求直接返回某个错误码。
微服务中的熔断是什么?
当电路中的负载过高的时候,“保险丝”就会熔断。微服务的熔断就如同保险丝一样,当服务间的调用出现频繁的超时,核心服务却一直在等待这个超时服务的响应结果,后果就是整个系统服务的卡顿、无反应,这对于用户端是不可接受的。所以熔断就是某个服务发生不断的调用响应超时的时候,就屏蔽掉这个服务,短路这个服务,不调用这个服务的具体内容直接返回一个默认值。
微服务中的降级是什么?
应该很多人都知道服务的限流吧,秒杀的时候,为了避免过高的并发压垮服务,一般系统都会对秒杀的请求做限流处理。这个就是典型的降级处理。
为了保证核心服务的正常运行,会对一些服务、接口、页面做降级处理,降级处理一般都是人工干预的,可以进行配置的。
熔断和降级的区别?
相同点
- 都是为了保证服务的可用性,防止系统发生崩溃
- 都导致了系统的某些服务、功能不可用
不同点
- 熔断是由某个下游服务故障引起的,降级一般从系统的整体负荷去考虑
Hystrix是怎么进行降级熔断的?
hystrix可以做什么用?
使用断路器隔离线程和信号量,提供实时的监控、报警。防止出现级联故障、快速失败和迅速恢复。
hystrix的隔离机制?
Hystrix是采用信号量/线程的方式进行资源的隔离,通过隔离限制依赖的并发量和阻塞扩散。
Hystrix 在用户请求和服务之间增加了一个线程池,用户的请求不会直接访问服务,而是通过Hystrix分配的线程池中的空闲线程来访问服务,如果线程池满了,默认不采用排队的方式,会直接进行降级处理。而且用户的请求不会无休止的阻塞,至少都会存在一个执行的返回结果(可以是一个友好的提示)。
信号量模式下,执行业务的线程和请求线程是同一个,不存在线程上下文切换带来的性能开销,信号量阻断并发访问指的是当前信号量有多少就允许多少线程去访问执行。
信号量的数量大小可以动态设置,线程池不允许。在使用线程池的时候,通过会通过自定义实现RequestInterceptor的接口来设置线程之间的请求头一致(比如请求头上携带的token)。
hystrix的熔断机制
如果某个服务存在大量的调用超时,此时就会对该服务做熔断处理,后续的所有请求都不在继续调用这个服务,而是直接返回一个提示,快速失败。间隔一段时间,如果目标服务情况好转后再恢复调用。
Circuit Breaker熔断器,熔断器是在线程池/信号量之前的组件,用户请求调用某一服务的时候,先经过熔断器,假如熔断器是打开的,则快速失败,请求不会再进入线程池。
熔断器相当于线程池之前的一个屏障,每个熔断器都会记录当前请求的调用成功、失败、超时、拒绝的次数。
熔断器存在三种状态:
Closed 关闭状态,调用失败次数积累到了阈值(或一定比例)则启动熔断机制;
Open 打开状态,这个时候对下游的调用都是内部直接返回错误,不走网络,但设计了一个时钟选项,默认的时钟达到了一定时间(这个时间一般设置成平均故障处理时间,也就是MTTR),到了这个时间,进入半熔断状态。
Half-Open 半熔断状态,允许定量的服务请求,如果调用都成功(或一定比例)则认为恢复了,关闭熔断器,否则认为还没好,又回到熔断器打开状态;
熔断在调用的服务方法前增加注解
服务降级,在Feign组件里面新增一个fallbackFactory方法。
Hystrix流程图
流程说明:
1、 每次调用的都会创建一个对象HystrixCommand,把依赖调用都封装到run方法里面。
2、执行execute/queue做同步或者异步调用。判断熔断器是否打开,如果打开则直接做降级策略,如果关闭则进行后续步骤。
3、判断线程池/信号量是否处于满状态,如果处于满状态则做降级策略,如果不是则调用对象HystrixCommand的run方法,执行业务逻辑。执行超时则降级处理。
4、计算熔断器状态,把所有的运行状态上报熔断器进行统计。
5、降级策略,抛出异常或者自定义处理方法。
Ribbon
在使用feign调用服务的时候,除了hystrix外还需要结合ribbon一起使用。通常ribbon起到的作用是设置连接时间、超时时间、调用服务的负载均衡等。
ribbon的一些配置:
#以下配置对服务hello-service-provider有效ribbon.eureka.enabled=true#建立连接超时时间,原1000ribbon.ConnectTimeout=60000#请求处理的超时时间,5分钟ribbon.ReadTimeout=60000#所有操作都重试ribbon.OkToRetryOnAllOperations=true#重试发生,更换节点数最大值ribbon.MaxAutoRetriesNextServer=10#单个节点重试最大值ribbon.MaxAutoRetries=1
Feign组件为什么要用接口调用远程接口呢?
feign是通过JDK的动态代理来实现的,对接口生成了对应的代理类进行请求处理和响应处理。
feign+ribbon使用的是RestTemplate来进行调用的,服务调用的时候IP+port传入的是服务名,需要去注册中心换成真实的IP+端口。在这个过程中,ribbon提供了一个负载均衡的处理方式LoadBalancerClient。
其实流程就是:服务启动的时候会注册到注册中心,ribbon会从注册中心获取到所有的服务列表和活跃的服务列表,当服务发生变化的时候,ribbon存在一个定时任务去获取、比对服务列表。
Feign在默认情况下使用的是JDK原生URLConnection发送HTTP请求,没有连接池,但是对每个地址会保持一个长连接,即利用HTTP的persistence connection。我们可以用Apache的HTTP Client替换Feign原始的http client,从而获取连接池、超时时间等与性能息息相关的控制能力。
我是凯腾凯,互联网浪潮下一枚苟且偷生的程序员