1.理解Hystrix
1.1.雪崩效应
在电影里面经常出现的场景,在冰山雪地不要大声呼喊,因为声音的震动会导致雪球的滑落,然后引起连锁反应导致整个雪山的崩塌这就是生活中的雪崩。在微服务里面也是一样,服务的调用非常复杂的 ,一个请求往往需要很多的微服务共同完成,可能会形成很长的服务调用链,在整个服务调用链中,某一个服务发生故障会导致调用它的服务跟着异常,然后导致整个调用链调用的异常,甚至导致整个微服务瘫痪 , — 这就是雪崩效应。如下图:
1.2.Hystrix介绍
Hystrix是国外知名的视频网站Netflix所开源的非常流行的高可用架构框架。Hystrix能够完美的解决分布式系统架构中打造高可用服务面临的一系列技术难题,如雪崩。
Hystrix是处理依赖隔离的框架,将出现故障的服务通过熔断、降级等手段隔离开来,这样不影响整个系统的主业务(比如你得了传染病是不是要把你关起来隔离呢),同时也是可以帮我们做服务的治理和监控。
Hystrix的英文是豪猪,中文翻译为 熔断器,其思想来源于我们家里的保险开关,当家里出现短路,保险开关及时切掉电路,保证家里人员的安全,其目的就是起保护作用。
其设计原则如下:
1.防止单个服务异常导致整个微服务故障。
2.快速失败,如果服务出现故障,服务的请求快速失败,线程不会等待。
3.服务降级,请求故障可以返回设定好的二手方案数据(兜底数据)。
4.熔断机制,防止故障的扩散,导致整个服务瘫痪。
5.服务监控,提供了Hystrix Bashboard仪表盘,实时监控熔断器状态
1.3.Hystrix的功能
资源隔离
资源隔离包括线程池隔离和信号量隔离,作用是限制调用分布式服务的资源使用,某一个调用的服务出现问题不会影响其他服务调用 ,这里可以简单的理解为资源隔离就是限制请求的数量。
就好比在肺炎疫情爆发期间,是不是要限制人口的流动量,流动量越大可能会导致更多的肺炎患者出现。
线程池隔离:使用一个线程池来存储当前请求,线程池对请求作处理,设置任务返回处理超时时间,堆积的请求先入线程池队列。这种方式要为每个依赖服务申请线程池,有一定的资源消耗,好处是可以应对突发流量(流量洪峰来临时,处理不完可将数据存储到线程池队里慢慢处理)
信号量隔离:使用一个原子计数器(或信号量)记录当前有多少个线程在运行,请求来先判断计数器的数值,若超过设置的最大线程个数则丢弃该类型的新请求,若不超过则执行计数操作请求来计数器+1,请求返回计数器-1。这种方式是严格的控制线程且立即返回模式,无法应对突发流量(流量洪峰来临时,处理的线程超过数量,其他的请求会直接返回,不继续去请求依赖的服务)
线程池和信号量对比:
线程池 | 线程池 | 信号量 |
线程 | 与调用线程非相同线程 | 与调用线程相同(jetty线程) |
开销 | 排队、调度、上下文开销等 | 无线程切换,开销低 |
异步 | 支持 | 不支持 |
并发支持 | 支持(最大线程池大小) | 支持(最大信号量上限) |
服务熔断
熔断机制是对服务链路的保护机制,如果链路上的某个服务不可访问,调用超时,发生异常等,服务会触发降级返回托底数据,然后熔断服务的调用(失败率达到某个阀值服务标记为短路状态),当检查到该节点能正常使用时服务会快速恢复。
简单理解就是当服务不可访问了或者服务器报错了或者服务调用超过一定时间没返回结果,就立马触发熔断机制配合降级返回预先准备的兜底数据返回,不至于长时间的等待服务的相应造成大量的请求阻塞,也不至于返回一些错误信息给客户端,而是返回一些兜底数据。
降级机制
超时降级、资源不足时(线程或信号量)降级,降级后可以配合降级接口返回托底数据。
简单理解就是服务降级就是当服务因为网络故障,服务器故障,读取超时等原因造成服务不可达的情况下返回一些预先准备好的数据给客户端。
在生活中服务降级到处可见,如在系统故障我们会返回友好的提示“服务暂时不可用”,或者如淘宝双11期间退款服务,留言服务暂时不可用,这个就是为了保证正常的购物流程相关服务没问题,然后人为的关闭一些不重要的服务,配合降级返回一些托底数据返回给用户(比如返回友好的提示信息“暂时不能退款”)。
缓存
提供了请求缓存、请求合并实现 , 在高并发的场景之下,Hystrix请求缓存可以方便地开启和使用请求缓存来优化系统,达到减轻高并发时请求线程的消耗、降低请求响应时间的效果。
1.4.Hystrix工作机制
正常情况下,断路器处于关闭状态(Closed),如果调用持续出错或者超时达到设定阈值,电路被打开进入熔断状态(Open),这时请求这个服务会触发快速失败(立马返回兜底数据不要让线程死等),后续一段时间内的所有调用都会被拒绝(Fail Fast),一段时间以后(withCircuitBreakerSleepWindowInMilliseconds=5s),保护器会尝试进入半熔断状态(Half-Open),允许少量请求进来尝试,如果调用仍然失败,则回到熔断状态,如果调用成功,则回到电路闭合状态;
就如同你生病了,你需要去医院看病,你的状态被标记为为“生病状态”(短路) ,你的朋友来找你玩,可能你的女朋友需要去回绝你的朋友说你生病了(托底,返回一些友好提示) , 当你的病好了,你的朋友又可以找你玩了(服务正常)。
2.Hystrix编码实战
在springcloud-order-server-1030工程的基础身上做修改,我们让Hystrix和Ribbon配合使用,下一章节我们讲Feign和Hystirx配合使用。
2.1.导入依赖
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-hystrix</artifactId></dependency>
2.2.配置类开启Hystrix
主配置类通过 @EnableCircuitBreaker 标签开启熔断功能
/*** 订单的启动类*///开启Hystrix熔断publicclassOrderServerApplication1030{ //省略...
2.3.方法熔断
通过 @HystrixCommand 标签标记方法熔断,标签的fallbackMethod属性指定拖地方法。那么当该方法在像远程服务发起调用出现异常,或是方法本身出现异常都会触发托底方法的执行,最终结果是托底方法的返回结果。
//浏览器调用该方法fallbackMethod="fallbackMethod") //方法熔断 (value="/order/{id}",method=RequestMethod.GET) (publicUsergetById( ("id")Longid){ //发送http请求调用 user的服务,获取user对象 : RestTemplate//user的ip,user的端口,user的Controller路径//String url = "http://localhost:1020/user/"+id;Stringurl="http://user-server/user/"+id; //发送http请求returnrestTemplate.getForObject(url, User.class); } //降级方法 , 参数和返回值必须和被熔断的方法一致 ,方法名要和 fallbackMethod 的值一致publicUserfallbackMethod( ("id")Longid){ //返回托底数据returnnewUser(-1L ,"无此用户","用户服务不可用"); }
我们可以在每个方法上打@HystrixCommand(fallbackMethod = “fallbackMethod”) 标签进行方法单独熔断,也可以在Controller使用 @DefaultProperties做统一配置,如
defaultFallback="fallbackMethod") //统一降级配置 (publicclassOrderController { //方法熔断value="/order/{id}",method=RequestMethod.GET) (publicUsergetById( ("id")Longid) //...省略...
2.4.托底方法
//托底方法publicUserfallbackMethod(){ //返回友好的提示信息returnnewUser(-1L,"无此用户","用户服务不可用"); }
注意:公共的托底方法不应该有参数。返回类型必须和熔断的方法的返回类型兼容,比如这里可以返回User或者User的子类。
在生产环境中我们可以让所有的方法都有相同的返回结果,如统一的JSON返回结果(JSONResult) ,那么在默认的降级方法中的返回类型就可以使用JSONResult了。
2.4.测试熔断
依次启动:springcloud-eureka-server-1010 , springcloud-user-server-1020 , springcloud-order-server-1030
浏览器访问 http://localhost:1030/order/1 ,当用户服务 springcloud-user-server-1020 正常启动的时候,订单服务是可以访问,浏览器也可以收到结果 , 当关闭掉用户服务 ,订单服务会触发熔断,返回托底数据
3.Hystrix参数配置
3.1.熔断参数配置
Hystrix提供了如下的几个关键参数(com.netflix.hystrix.HystrixCommandProperties),来对一个熔断器进行配置:
hystrix.command.default.circuitBreaker.requestVolumeThreshold
滑动窗口的大小,默认为20hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds
过多长时间熔断器再次检测是否开启默认5000(5s)hystrix.command.default.circuitBreaker.errorThresholdPercentage
错误率,默认50%
3个参数放在一起,所表达的意思就是:
每当20个请求中,有50%失败时,熔断器就会打开,此时再调用此服务,将会直接返回失败,不再调远程服务。直到5s钟之后,重新检测该触发条件,判断是否把熔断器关闭,或者继续打开。
hystrix command default circuitBreaker requestVolumeThreshold 20 #20个请求中 errorThresholdPercentage 50 #出错百分比阈值 sleepWindowInMilliseconds 50000 #短路5秒钟,尝试恢复
3.2.超时设置
我们知道在服务的远程调用过程中,如果调用时间过久会触发Ribbon的超时,Hystrix也有超时配置 hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds
我们往往会把Ribbon和Hystrix的超时配合起来配置:
ribbon MaxAutoRetries 1 #最大重试次数 MaxAutoRetriesNextServer 1 #切换实例的重试次数 OkToRetryOnAllOperations false # 对所有的操作请求都进行重试, ConnectTimeout 1000 #请求连接的超时时间 ReadTimeout 1800 #请求处理的超时时间hystrix command default execution isolation thread timeoutInMilliseconds 3000 #hystrix超时,置thread和semaphore两种隔离策略的超时时间
需要注意的是,我们设置了重试机制就需要Hystrix超时时间大于Ribbon超时时间,因为Hystrix超时后会直接熔断就不会再出发重试
hystrix超时=(1 + MaxAutoRetries + MaxAutoRetriesNextServer) * ReadTimeout
如果要针对于某个API做单独的超时设置也可以通过如下方式单独指定
commandProperties= { (name="execution.isolation.thread.timeoutInMilliseconds", value="3000") (}) value="/order/{id}",method=RequestMethod.GET) (publicUsergetById( ("id")Longid){ ...
3.3.资源隔离模式
在Hystrix可以通过execution.isolation.strategy
配置切换资源隔离模式:线程池或者信号量
hystrix command default execution isolation strategy THREAD #默认是线程池,可以修改为信号量: SEMAPHORE
除了上面方式也可以使用如下方式单独指定:
fallbackMethod="", commandProperties= (name="execution.isolation.strategy", value="THREAD") ) (value="/order/{id}",method=RequestMethod.GET) (publicUsergetById( ("id")Longid){ ...
3.4.最大请求设置
通过 execution.isolation.semaphore.maxConcurrentRequests 配置信号量模式下的最大请求数量(并发)
hystrix command default execution isolation thread timeoutInMilliseconds 30000 #hystrix超时 strategy SEMAPHORE #默认是线程池,可以修改为信号量: SEMAPHORE semaphore maxConcurrentRequests 10 # SEMAPHORE模式下 , 1秒钟最大请求数量,默认10
在线程池模式下通过 hystrix.threadpool.default.coreSize
设置线程并发数量,
通过hystrix.threadpool.default.maxQueueSize
设置线程池队列模式,通过 hystrix.threadpool.default.queueSizeRejectionThreshold
设置排队数量 ,见:com.netflix.hystrix.HystrixThreadPoolProperties
hystrix threadpool default coreSize 10 #最大线程数 , 并发执行的最大线程数,默认10 maxQueueSize -1 #最大排队长度。默认-1,使用SynchronousQueue。其他值则使用 LinkedBlockingQueue。 queueSizeRejectionThreshold5 ...
3.5.Fallback设置
并发达到 hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests
配置的值时,fallback会被被调用,模式10
通过配置hystrix.command.default.fallback.enabled
开启fallback ,即请求失败尝试走托底
hystrix command default fallback enabled true #请求失败时是否要走托底 isolation semaphore maxConcurrentRequests 10 #fallback最大并发数量