简介
Hystrix是国外知名的视频网站Netflix所开源的非常流行的高可用架构框架。Hystrix能够完美的解决分布式系统架构中打造高可用服务面临的一系列技术难题。Hystrix “豪猪”,具有自我保护的能力。
Hystrix工作原理
产生背景
雪崩效应
首先,学习hystrix 之前,我们先得了解一下什么雪崩效应。
在微服务架构中,我们把每个业务都拆成了单个服务模块,然后当有业务需求时,服务间可互相调用,但是,由于网络原因或者其他一些因素,有可能出现服务不可用的情况,当某个服务出现问题时,其他服务如果继续调用这个服务,就有可能出现线程阻塞,但如果同时有大量的请求,就会造成线程资源被用完,这样就可能会导致服务瘫痪,由于服务间会相互调用,很容易造成蝴蝶效应导致整个系统宕掉。
造成雪崩效应的实质原因是由于服务堆积在同一个线程池中,所有的请求都是同一个线程池进行处理,这时候如果在高并发情况下,所有的请求全部访问同一个接口,这时候可能会导致其他服务没有线程进行接受请求
如下图所示:
略
解决方案
因此,就有人提出来一些解决方案来解决这一问题,这里只列举5种。
- 超时机制
- 服务隔离
- 服务限流
- 服务熔断
- 服务降级
这五种我在这里大致的讲一下,等有时间我会专门开一篇文章详细的介绍和验证。
超时机制
服务级联失败(服务雪崩效应)的最根本原因是:大量请求线程同步等待造成的资源耗尽那么,在不做任何处理的情况下,服务提供者不可用会导致消费者请求线程强制等待,而造成系统资源耗尽,而且,既然服务提供者已经不可用了,还在作死的请求的话,是毫无意义的。那么,如果我们加入超时机制,例如2s,那么超过2s就会直接返回了,那么这样是不是一定程度上可以抑制消费者资源耗尽的问题。
服务隔离
因为默认情况下,只有一个线程池会维护所有的服务接口,如果大量的请求访问同一个接口,达到tomcat 线程池默认极限,可能会导致其他服务无法访问。
如下图所示:
略
隔离方式常见的有两种,一种是线程池隔离(每个服务接口,都有自己独立的线程池,每个线程池互不影响)。 另一种是信号量隔离(使用一个原子计数器或信号量来记录当前有多少个线程在运行,当请求进来时先判断计数器的数值,若超过设置的最大线程个数则拒绝该请求,若不超过则通行,这时候计数器+1,请求返 回成功后计数器-1)。
服务限流
服务限流就是对接口访问进行限制,常用服务限流算法令牌桶、漏桶。计数器也可以进行粗暴限流实现。
服务熔断
远程服务不稳定或网络抖动时暂时关闭,就叫服务熔断。可能有些人还是不懂,举个通俗易懂的例子,就跟我们现实生活中的“跳闸”一样,跳闸同学们应该都听说过吧,比如说家里有点短路了,那是不是会跳闸,等你把短路的问题找到并且修复后,然后你把这个闸闭合,是不是整个家庭的电路又恢复了正常,这就是熔断器。熔断器就是保护服务高可用的最后一道防线。所以,同样的道理,当依赖服务有大量超时时,在让新的请求去访问根本没有意义,只会无畏的消耗现有资源。比如我们设置了超时时间为1s,如果短时间内有大量请求在1s内都得不到响应,就意味着这个服务出现了异常,此时就没有必要再让其他的请求去访问这个依赖了,这个时候就应该使用熔断器避免资源浪费。
熔断器开关相互转换的逻辑如下图:
服务的健康状况 = 请求失败数 / 请求总数.
熔断器开关由关闭到打开的状态转换是通过当前服务健康状况和设定阈值比较决定的.
1、当熔断器开关关闭时, 请求被允许通过熔断器. 如果当前健康状况高于设定阈值, 开关继续保持关闭. 如果当前健康状况低于设定阈值, 开关则切换为打开状态.
2、当熔断器开关打开时, 请求被禁止通过
3、当熔断器开关处于打开状态, 经过一段时间后, 熔断器会自动进入半开状态, 这时熔断器只允许一个请求通过. 当该请求调用成功时, 熔断器恢复到关闭状态. 若该请求失败, 熔断器继续保持打开状态, 接下来的请求被禁止通过.
熔断器的开关能保证服务调用者在调用异常服务时, 快速返回结果, 避免大量的同步等待.并且熔断器能在一段时间后继续侦测请求执行结果, 提供恢复服务调用的可能
服务降级
有服务熔断,必然要有服务降级。所谓降级,就是当某个服务熔断之后,服务将不再被调用,此时客户端可以自己准备一个本地的fallback(回退)回调,返回一个缺省值。 例如:(备用接口/缓存/mock数据)这样做,虽然服务水平下降,但好歹可用,比直接挂掉要强,当然这也要看适合的业务场景。
Hystrix内部工作流程
下图为Hystrix服务调用的内部逻辑:
- 构建Hystrix的Command对象, 调用执行方法.
- Hystrix检查当前服务的熔断器开关是否开启, 若开启, 则执行降级服务getFallback方法.
- 若熔断器开关关闭, 则Hystrix检查当前服务的线程池是否能接收新的请求, 若线程池已满, 则执行降级服务getFallback方法.
- 若线程池接受请求, 则Hystrix开始执行服务调用具体逻辑run方法.
- 若服务执行失败, 则执行降级服务getFallback方法, 并将执行结果上报Metrics更新服务健康状况.
- 若服务执行超时, 则执行降级服务getFallback方法, 并将执行结果上报Metrics更新服务健康状况.
- 若服务执行成功, 返回正常结果.
- 若服务降级方法getFallback执行成功, 则返回降级结果.
- 若服务降级方法getFallback执行失败, 则抛出异常.
案例搭建
示例代码
建立hystrix-dashboard-turbine子工程
服务注册中心
前几节已经演示过,这里就不多讲了,如需要了解请点击进入
服务消费方
1.pom中添加依赖
<!-- hystrix断路器 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
2.application.yml 配置文件
#服务启动端口号 server: port: 9001 #服务名称(服务注册到eureka名称) spring: application: name: consumer #客户端注册进eureka服务列表内 eureka: client: service-url: defaultZone: http://eureka7001:7001/eureka #该应用为注册中心,不会注册自己,默认true register-with-eureka: true #是否需要从eureka上获取注册信息,默认true fetch-registry: true
3.服务接口
package net.riking.springcloud.consumer.controller; import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext; import net.riking.springcloud.consumer.service.impl.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/user/consumer") public class UserConsumerController { @Autowired UserService userService; @GetMapping public String consumer(@RequestParam String username) { HystrixRequestContext context = HystrixRequestContext.initializeContext(); String result = userService.consumer(username); context.close(); return "消费服务:"+result ; } @GetMapping("/port") public String port() { HystrixRequestContext context = HystrixRequestContext.initializeContext(); String result = userService.port(); context.close(); return "消费服务:"+result ; } }
4.服务降级处理
package net.riking.springcloud.consumer.service.impl; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.client.RestTemplate; @Service public class UserService { @Autowired private RestTemplate restTemplate; private static final String REST_URL_PREFIX = "http://PROVIDER"; @HystrixCommand(fallbackMethod = "consumerFallback")//服务降级处理 @CacheResult//开启请求缓存功能 public String consumer(String username) { String result = restTemplate.getForObject(REST_URL_PREFIX+"/user/provider?username="+username, String.class); return "消费服务:"+result ; } @HystrixCommand(fallbackMethod = "userFallback")//服务降级处理 @CacheResult//开启请求缓存功能 public String port() { String result = restTemplate.getForObject(REST_URL_PREFIX+"/user/provider/port", String.class); return "消费服务:"+result ; } //服务降级处理实现 public String consumerFallback(String username) { return "系统繁忙,请稍候再试"; } //服务降级处理实现 public String userFallback() { return "系统繁忙,请稍候再试"; } }
5.启动HystrixConsumerApplication服务
package net.riking.springcloud.consumer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication @EnableDiscoveryClient @EnableCircuitBreaker //开启服务保护机制 public class HystrixConsumerApplication{ public static void main(String[] args) { SpringApplication.run(HystrixConsumerApplication.class, args); } @Bean @LoadBalanced //如果提供者服务为集群,当在请求时,拥有客户端负载均衡的能力 RestTemplate restTemplate() { return new RestTemplate(); } }
服务提供方
由于在前一结上进行整合,这里就不多讲了,如需要了解请点击进入
Hystrix 服务保护机制验证
服务降级验证
由于电脑死机重启,以后有时间演示
服务隔离验证
服务熔断验证
源码分析
先把一些基础的知识先归纳好,这个以后有时间在写,