首发地址:什么是Hystrix,阿里技术最终面,遗憾的倒在Hystrix面前 !面试
什么是服务雪崩?
在分布式架构中,很常见的一个情形就是某一个请求须要调用多个服务。sql
如客户端访问 user 服务,而 user 服务须要调用 order 服务,order 服务须要调用 goods 服务,因为网络缘由或者自身的缘由,若是 order 服务或者 goods 服务不能及时响应,user 服务将处于阻塞状态,直到 order 服务 goods 服务响应。数据库
此时如有大量的请求涌入,容器的线程资源会被消耗完毕,致使服务瘫痪。缓存
服务与服务之间的依赖性,故障会传播,形成连锁反应,会对整个微服务系统形成灾难性的严重后果,这就是服务故障的“雪崩”效应。tomcat
1.如图所示,此时的系统正在愉快的运行中。服务器
2.忽然这个时候,goods服务节点的网络发生了故障。goods服务节点瘫痪,goods服务不可用。网络
3.因为good服务瘫痪致使order服务向goods服务发送的请求得不到返回,一直处于阻塞,此时user服务仍然一直 向order服务发送请求,最终致使order服务节点的资源耗尽,order服务节点瘫痪,order服务不可用。架构
4.因为good服务瘫痪致使order服务向goods服务发送的请求得不到返回,一直处于阻塞,此时user服务仍然一直 向order服务发送请求,最终致使order服务节点的资源耗尽,也瘫痪掉。此时user服务向order服务发送的请求一样也得不到返回,而客户端依然源源不断的向user服务节点发送请求,最终user服务节点和order服务节点同样,因为资源耗尽致使服务器瘫痪,user服务也不可用。并发
如上所述,一个服务节点的瘫痪,致使整条链路的服务节点都瘫痪的情形,咱们称之为服务雪崩。app
为何会产生服务雪崩?
流量激增:好比异常流量、用户重试致使系统负载升高;
缓存穿透:假设A为client端,B为Server端,假设A系统请求都流向B系统,请求超出了B系统的承载能力,就会形成B系统崩溃;
程序有Bug:代码循环调用的逻辑问题,资源未释放引发的内存泄漏等问题;
硬件故障:好比宕机,机房断电,光纤被挖断等。
数据库严重瓶颈,好比:长事务、慢sql等。
线程同步等待:系统间常常采用同步服务调用模式,核心服务和非核心服务共用一个线程池和消息队列。若是一个核心业务线程调用非核心线程,这个非核心线程交由第三方系统完成,当第三方系统自己出现问题,致使核心线程阻塞,一直处于等待状态,而进程间的调用是有超时限制的,最终这条线程将断掉,也可能引起雪崩。
有什么办法解决服务雪崩?
当发生突发流量激增的状况下,咱们可使用自动扩容,或者是在负载均衡器中添加服务限流功能
对于缓存穿透形成的服务雪崩问题,能够经过缓存预加载、缓存异步加载等方式来解决。
对于程序bug,emmm...,只能改bug了。
对于数据库查询时间过长致使的服务雪崩能够进行sql优化,硬件升级等。
对于硬件故障形成的服务雪崩, ,跨机房路由,异地多活等方式。
对于不一样的形成服务雪崩的场景,有着不少不一样的解决方案,可是没有一个通用的解决方案能够解决全部的问题。
在经过大量的实践证实,线程同步等待是最多见引起的雪崩效应的场景,此刻,本章节的主人公Hystrix将粉墨登场,咱们将详细介绍如何使用Hystrix作故障隔离,熔断器机制等能够解决依赖服务不可用的问题。
Hystrix总体认知
Hystrix是一个用于处理微服务架构中的服务之间调用故障和容错的开源库。
在微服务架构里,各个服务之间的调用均可能会失败,好比超时、异常等。
Hystrix可以保证在一个依赖出问题的状况下,不会致使整个服务链路全线崩溃,提升微服务架构的可用性。
Hystrix,咱们又称“断路器”,其自己是一种开关装置,当某个服务单元发生故障以后,经过断路器的故障监控(相似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方没法处理的异常,这样就保证了服务调用方的线程不会被长时间、没必要要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
Hystrix设计目标,实现方式
设计目标
(1)对依赖服务调用时出现的调用延迟和调用失败进行控制和容错保护。
(2)阻止某一个依赖服务的故障在整个系统中蔓延,服务A->服务B->服务C,服务C故障了,服务B也故障了,服务A故障了,整个系统所有故障,总体宕机。(3)提供fail-fast(快速失败)和快速恢复的支持。
(4)提供fallback优雅降级的支持。
(5)支持近实时的监控、报警以及运维操做。
实现方式
-
经过hystrixCommand或者HystrixObservableCommand来封装对外部依赖的访问请求,这个访问请求通常会运行在独立的线程中。
-
对于超出咱们设定的阈(yu)值服务调用,直接进行超时返回,不容许它长时间的阻塞。
-
对每个依赖服务进行资源隔离。经过线程池或者是semaphore这两种方式。
-
对依赖服务被调用的成功次数,失败次数,拒绝次数,超时次数进行统计。
-
若是对某一个依赖服务的调用失败次数超过了一点的阈值,Hystrix自动进行熔断,并在一段时间内对该服务的调用直接进行降级,一段时间后再自动尝试恢复
-
当对一个服务调用出现失败、被拒绝、超时、短路等异常状况时,自动调用fallback降级机制。
-
对属性和配置的修改提供近实时的支持
Hystrix工做流程
首先咱们看一下管网的Hystrix工做流程图:
下面咱们针对这张图详细解读下Hystrix的工做流程。
1.每次调用都会建立HystrixCommand或者HystrixObservableCommand对象
2.执行execute(observe)或queue(toObservable)作同步\异步调用
3.检查请求结果是否被缓存,若是缓存直接返回
4.检查是否开启了断路器,若是开启直接跳到步骤8
5.检查线程池/信号量是否跑满,若是跑满进入步骤8
6.执行 HystrixObservableCommand.construct() or HystrixCommand.run(),若是执行异常或者调用超时直接跳到步骤8
7.计算断路器状态,全部的运行状态(成功, 失败, 拒绝,超时)上报给断路器,用于统计从而判断断路器状态
8.调用fallback降级机制,经过上述步骤会有(熔断器打开,线程池/信号量跑满,调用超时,调用失败)四种状况会进行降级处理
9.返回依赖请求的真正结果
工做流程详解
第一步,建立HystrixCommand或者HystrixObservableCommand对象
HytrixCommand和HystrixObservableCommand包装了对外部依赖访问的逻辑。
整个流程的第一个步骤就是实例化HystrixCommand或者HystrixObservableCommand对象。
在构造这两个Command对象时,能够经过构造方法传递任何执行过程当中须要的参数。
若是对外部依赖调用只返回一个结果值,那么能够实例化一个HystrixCommand对象。
HystrixCommand command = newHystrixCommand(arg1, arg2);
若是在调用外部依赖时须要返回多个结果值时,能够实例化一个HystrixObservableCommand对象
HystrixObservableCommand command = newHystrixObservableCommand(arg1, arg2);
第二步,执行execute(observe)或queue(toObservable)作同步\异步调用
HystrixCommand主要是使用如下两个命令
execute():同步执行,从依赖的服务返回一个单一的结果对象,或者是在发生错误的时候抛出异常。
queue():异步执行,直接返回一个Future对象,其中包含了服务执行结束时要返回的单一结果对象。
HystrixObservableCommand使用如下两个命令
observe():返回Observable对象,返回 Observable 对象,当即发出请求,在依赖服务响应(或者抛出异常/超时)时,经过注册的 Subscriber 获得返回结果,它是一个Hot Observable。
toObservable():返回Observable对象,但只有在订阅该对象时,才会发出请求,而后在依赖服务响应(或者抛出异常/超时)时,经过注册的 Subscriber 获得返回结果,它是一个Cold Observable。
第三步,检查请求结果是否被缓存,若是缓存直接返回
若当前命令的请求缓存功能是被启用的,而且该命令缓存命中,那么缓存的结果会当即以Observable对象的形式返回。
这个结果缓存的好处为:
一、在同一个请求上下文中,能够减小使用相同参数请求原始服务的开销。
二、请求缓存在步骤5执行以前生效,因此能够有效减小没必要要的线程开销。
第四步,检查是否开启了断路器
在缓存没有被命中时,Hystrix会在执行步骤5以前先检查断路器是否被打开。若是打开了,Hystrix不会执行任何命令执行跳转到步骤8
断路器开关控制条件: 1.对外部依赖调用的次数知足配置的阈值 2.对外部依赖调用发生错误的比率知足配置的阈值
在知足以上两个条件时,断路器打开熔断开关,以后全部对外部依赖调用都将被直接断开。
在开关打开时长超过试探窗口期后,断路器将尝试放行部分外部依赖的调用,
根据试探的结果决定从新开启或者关闭熔断开关。
第五步,检查线程池/信号量是否跑满
咱们知道,Hystrix引入了线程池和信号量两种方式实现资源隔离机制。若是此时命令对应的线程池或队列或信号量已经满了,直接跳转到步骤8。
第六步,执行 HystrixObservableCommand.construct() or HystrixCommand.run()
Hystrix会根据咱们编写的方法来决定采起什么方式去请求依赖服务。
1.HystrixCommand.run()——返回单个响应或抛出异常。
2.HystrixObservableCommand.construct()——返回 Observable 对象来发射多个结果,或经过onError发送错误通知。
若是run()或construct()方法执行时长超过了命令的超时阀值,其线程将抛出一个TimeoutException(或者在一个单独的线程抛出,若是命令没有运行在它本身的线程)。
这种状况下 Hystrix转接到fallback处理逻辑(第8步)。
而且若是该命令没有取消或中断,它将放弃run()或construct()方法最终的返回值。
若是命令没有抛出异常而且返回了响应,Hystrix 将会在执行一些日志记录和度量报告以后返回结果给调用者。
若是是经过run()运行,Hystrix 将返回 Observable 发射单个结果,而后发送一个onCompleted的通知;若是是经过construct()运行,Hystrix 直接返回该方法产生的Observable对象。
第七步,计算断路器状态
Hystrix会将每个依赖服务的调用成功,失败,拒绝,超时,等事件,都会发送给circuit breaker断路器。
HystrixCircuitBreaker经过维护一系列的counter记录外部依赖请求的执行状况。
断路器根据维护的这些信息,在符合触发条件下开启断路功能,在条件合适的时候关闭断路开关。
若是打开了断路器,那么在一段时间内就会直接短路,而后若是在以后第一次检查发现调用成功了,就关闭断路器。
第八步,调用fallback降级机制
经过对上述步骤的详细解读,咱们发现有如下几种状况是会调用fallback降级机制的。
1.断路器打开
2.线程池或者信号量已经满了
3.command执行异常
4.执行超时
在服务降级逻辑中,须要实现一个通用的响应结果,而且该结果的处理逻辑应当是从缓存或是根据一些静态逻辑来获取,而不是依赖网络请求获取。
若是必定要在服务降级逻辑中包含网络请求,那么该请求也必须包装在HystrixCommand或HystrixObservableCommand中,从而造成级联的降级策略。
而最终的降级逻辑必定不是一个依赖网络请求的处理,而是一个可以稳定的返回结果的处理逻辑。
1.在 HystrixCommand 中,在 HystrixCommand.getFallback()方法中提供自定义的回调逻辑,方法返回单个回调值。
2.在 HystrixObservableCommand 中,在HystrixObservableCommand.resumeWithFallback() 方法中提供自定义的回调逻辑,方法返回一个Observable对象来发射一个或多个降级结果
若是fallback返回告终果,那么Hystrix就会返回这个结果。
对于HystrixCommand,会返回一个Observable对象,其中会发返回对应的结果;
对于HystrixObservableCommand,会返回一个原始的Observable对象。
若是没有实现fallback,或者是fallback抛出了异常,Hystrix会返回一个Observable,可是不会返回任何数据。
不一样的command执行方式,其fallback为空或者异常时的返回结果不一样
1.对于execute(),直接抛出异常 2.对于queue(),返回一个Future,调用get()时抛出异常 3.对于observe(),返回一个Observable对象,可是调用subscribe()方法订阅它时,抛出调用者的onError方法 4.对于toObservable(),返回一个Observable对象,可是调用subscribe()方法订阅它时,抛出调用者的onError方法
第九步,返回依赖请求的真正结果
若是Hystrix命令执行成功,它将以Observable形式返回响应给调用者。根据你在步骤2的调用方式不一样,在返回Observablez以前可能会作一些转换。
execute():经过调用queue()来获得一个Future对象,而后调用get()方法来获取Future中包含的值。
queue():将Observable转换成BlockingObservable,在将BlockingObservable转换成一个Future。
observe():订阅返回的Observable,而且当即开始执行命令的逻辑。
toObservable():返回一个没有改变的Observable,你必须订阅它,它才可以开始执行命令的逻辑。
上述就是整个Hystrix的工做流程,固然没有很深刻的讲解,可是仍是建议多看几遍,我面试的时候碰到好几回让我简述Hystrix工做流程,多看几遍,记在内心,面试不慌。
Hystrix使用框架搭建
固然了,Hystrix也能和Feign 和 Zuul 的集成使用,这些在这里就不赘述了,后续介绍Feign和Zuul的文章中会详细说明。
本文主要介绍HystrixCommand 注解方式的使用。
首先咱们搭建一个HystrixClient项目。
添加配置文件application.properties
新建RestConfiguration类,用来全局配置RestTemplate。
新建HystrixController类
而后在启动类的上添加@EnableHystrix注解。
改造上一篇万字详解Ribbon架构,针对面试高频题多角度细说Ribbon中提供的OrderService,让代码休眠5秒后在返回。
在HystrixController中的三个方法中分别配置了2000ms,10000ms,10000ms若是没有返回结果,那么将直接回调用咱们指定的fallback。
OrderService
上述三步,基本的Hystrix使用框架就搭建完成了,而后咱们启动上一篇万字详解Ribbon架构,针对面试高频题多角度细说Ribbon中提到的Eureka-Server,并按照上篇文章的启动方式,分别启动OrderServeice的7777,8888,9999三个端口,此时咱们打开 http://localhost:8761/ 页面,咱们发现已经有三个服务名为order-service,端口号分别7777,8888,9999的服务注册了进来。
最后咱们启动HystrixClient启动类,而后咱们先访问设置超时时间为10000ms的 localhost:8088/test2 ,由于咱们在OrderService中设置的休眠时间为3000ms因此能在超时时间内返回请求,因此不用调用fallback。
Hystrix核心配置详解
Execution相关的属性的配置
hystrix.command.default.execution.isolation.strategy
隔离策略,默认是Thread, 可选Thread,Semaphore
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds
命令执行超时时间,默认1000ms。
hystrix.command.default.execution.timeout.enabled
执行是否启用超时,默认启用true。
hystrix.command.default.execution.isolation.thread.interruptOnTimeout
发生超时是是否中断,默认true。
hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests
理论上选择semaphore size的原则和选择thread size一致,但选用semaphore时每次执行的单元要比较小且执行速度快(ms级别),不然的话应该用thread。semaphore应该占整个容器(tomcat)的线程池的一小部分。
Fallback相关配置
hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests
若是并发数达到该设置值,请求会被拒绝和抛出异常而且fallback不会被调用。默认10。
hystrix.command.default.fallback.enabled
当执行失败或者请求被拒绝,是否会尝试调用hystrixCommand.getFallback() 。默认true。
Metrics相关属性配置
hystrix.command.default.metrics.rollingStats.timeInMilliseconds
设置统计的时间窗口值的,毫秒值,circuit break 的打开会根据1个rolling window的统计来计算。若rolling window被设为10000毫秒,则rolling window会被分红n个buckets,每一个bucket包含success,failure,timeout,rejection的次数的统计信息。默认10000。
hystrix.command.default.metrics.rollingStats.numBuckets
设置一个rolling window被划分的数量,若numBuckets=10,rolling window=10000,那么一个bucket的时间即1秒。必须符合rolling window % numberBuckets == 0。默认10。
hystrix.command.default.metrics.rollingPercentile.enabled
执行时是否enable指标的计算和跟踪,默认true。
hystrix.command.default.metrics.rollingPercentile.timeInMilliseconds
设置rolling percentile window的时间,默认60000。
hystrix.command.default.metrics.rollingPercentile.numBuckets
设置rolling percentile window的numberBuckets。逻辑同上。默认6。
hystrix.command.default.metrics.rollingPercentile.bucketSize
若是bucket size=100,window=10s,若这10s里有500次执行,只有最后100次执行会被统计到bucket里去。增长该值会增长内存开销以及排序的开销。默认100。
hystrix.command.default.metrics.healthSnapshot.intervalInMilliseconds
记录health 快照(用来统计成功和错误绿)的间隔,默认500ms。
ThreadPool 相关属性配置
hystrix.threadpool.default.coreSize
并发执行的最大线程数,默认10。
hystrix.threadpool.default.maxQueueSize
BlockingQueue的最大队列数,当设为-1,会使用SynchronousQueue,值为正时使用LinkedBlcokingQueue。该设置只会在初始化时有效,以后不能修改threadpool的queue size,除非reinitialising thread executor。默认-1。
hystrix.threadpool.default.queueSizeRejectionThreshold
即便maxQueueSize没有达到,达到queueSizeRejectionThreshold该值后,请求也会被拒绝。由于maxQueueSize不能被动态修改,这个参数将容许咱们动态设置该值。if maxQueueSize == -1,该字段将不起做用。
hystrix.threadpool.default.keepAliveTimeMinutes
若是corePoolSize和maxPoolSize设成同样(默认实现)该设置无效。
hystrix.threadpool.default.metrics.rollingStats.timeInMilliseconds
线程池统计指标的时间,默认10000。
hystrix.threadpool.default.metrics.rollingStats.numBuckets
将rolling window划分为n个buckets,默认10。
Hystrix容错机制之回退降级
1.什么是降级?
降级,一般指事务高峰期,为了保证核心服务正常运行,须要停掉一些不过重要的业务,或者某些服务不可用时,执行备用逻辑从故障服务中快速失败或快速返回,以保障主体业务不受影响。
Hystrix提供的降级主要是为了容错,保证当前服务不受依赖服务故障的影响,从而提升服务的健壮性。
要支持回退或降级处理,能够重写HystrixCommand的getFallBack方法或HystrixObservableCommand的resumeWithFallback方法。
2.什么状况下才会走降级?
从Hystrix的工做流程图中咱们能够看到如下状况会走降级逻辑。
1.断路器打开
2.线程池或者信号量已经满了
3.command执行异常
4.执行超时
3.回退降级有哪些处理方式?
-
快速失败:发生故障后直接抛出,不作处理。
-
无声失败:发生故障后,返回无心义内容,如null,空Map等,故障会被屏蔽。
-
静态失败:这种配置下,发生故障会返回静态的默认值,如返回值是boolean,则结果为默认true。
-
Stubbed:这种配置适用于返回值是一个复合对象的情形,发生故障时,会手动建立一个复合对象的实例,实例中每每包含了一些默认值或错误信息。
-
依赖缓存:这种状况下,当下层服务故障时,会从缓存中取得以前的旧数据供使用。
-
主次模式:这是回退降级的一种特殊使用方法。
主次模式解释:
有时候,咱们可能会遇到这样的场景。针对某个业务,可能会有两种处理方案,A方案高效,可是没有通过规模化测试,不敢保证可靠性。B方案保守,虽然效率较低,可是不会出现。这时候,咱们就能够尝试采用主次模式。主流程基于A方案运行,fallback基于B方案运行。在运行过程当中,如不出错,则一直使用A方案,一时出错,可经过回退降级,迅速切换为B方案,以免问题的不受控扩散。
Hystrix监控系统搭建
什么是Hystrix Dashboard?
Hystrix提供了准实时的调用监控(Hystrix DashBoard),Hystrix会持续的记录经过Hystrix发起的请求的执行信息,以统计报表和图形的形式展现给客户,包括每秒执行多少,请求多少成功,请求失败多少等。
Netflix经过Hystrix-metics-event-stream项目实现了对以上指标的监控,SpringCloud也提供了Hystrix DashBoard的整合,对监控内容转化成可视化的界面,以便于用户可以直接的看到服务和集群的状态,在实际使用中,咱们每每还要结合Turbine来使用。
开始搭建Hystrix Dashboard
Hystrix Dashboard的搭建其实很简单,分为三步:
-
建立监控Hystrix Dashboard项目模块
-
配置application.yml
-
配置启动类
首先咱们建立一个HystrixDashboard项目,配置pom.xml文件以下
配置端口为9001
server.port = 9001
配置启动类,添加@EnableHystrixDashboard注解
启动后访问http://localhost:9001/hystrix
只要咱们能看到一只豪猪就说明启动成功了。
Hystrix使用总结
本文从介绍服务雪崩引入Hystrix,首先带领你们对Hystrix进行了一个总体认知,而且介绍了Hystrix的设计目标以及实现方式。
而后详细分九步了Hystrix的整个工做流程,而且带领你们实战搭建了Hystrix框架和Hystrix监控系统。
最后详细介绍了Hystrix的核心配置,以及Hystrix的重中之重之回退降级。
Hystrix断路器一样是SpringCloud生态系统中不可缺乏的一环,一样也是面试中常常会出现的高频面试题。
原创不易,若是你们喜欢,赏个分享点赞在看三连吧。和你们一块儿成为这世界上最优秀的人。