今天和大家分享 Hystrix 组件的使用,其功能主要包含:服务雪崩、服务隔离策略、服务降级、数据监控、熔断等作用。下面我们以此介绍其使用方法。
一、服务雪崩:
服务雪崩是一种现象,微服务之间调用时,被调用的某个节点相应变慢,或者出现异常,在高并发情景下占用大量tomcat连接数,导致连接资源耗尽,此微服务响应迅速变得延时,导致该服务节点不能正 常的接收到正常的情况,这就是服务雪崩行为。后面讲的服务隔离就是针对并发量高的接口通过控制单位时间内的请求数,来避免服务雪崩的。
一、服务隔离及策略:
如果整个系统雪崩是由于一个接口导致的,由于这一个接口响应不及时导致问题,那么我们 就有必要对这个接口进行隔离,就是只允许这个接口最多能接受多少的并发,
做了这样的限 制后,该接口的主机就会空余线程出来接收其他的情况,不会被哪个坏了的接口占用满。 Hystrix 就是一个很好的服务隔离框架。
1、使用方法如下:
引入jar:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
启动类开启Hystrix: 加注解 @EnableCircuitBreaker 如下:
import com.xiangxue.jack.service.feign.StudentService; import com.xiangxue.jack.service.feign.TeacherServiceFeign; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication(scanBasePackages = {"com.nandao.apply"}) //注册到eureka @EnableEurekaClient //重点:开启断路器功能,这里就是开启Hystrix @EnableCircuitBreaker //开启重试功能 //@EnableRetry public class MicroWebnoApplication { public static void main(String[] args) { SpringApplication.run(MicroWebnoApplication.class,args); } }
简单业务代码demo如下:主要是 @HystrixCommand 注解
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.springframework.stereotype.Service; @Service public class TicketServiceImpl implements TicketService { @HystrixCommand @Override public String queryUser() { return "queryUser"; } }
这就是最简单的服务隔离使用流程
2、依据实战代码深入分析两种隔离策略配置
A:线程池隔离策略
线程池隔离策略 ,创建独立线程接收请求,系统默认配置是10个并发,系统默认采用的就是线程池隔离。
注意:当hystrix断路器的隔离策略为THREAD时,是无法取得ThreadLocal中的值的。
代码如下:
/* execution.isolation.strategy 执行的隔离策略 * THREAD 线程池隔离策略 独立线程接收请求 SEMAPHORE 信号量隔离策略 在调用线程上执行 queryContentsFallback 是服务降级后走的方法 后面会讲 */
@HystrixCommand(fallbackMethod = "queryContentsFallback",
commandKey = "queryContents",
groupKey = "querygroup-one",
commandProperties = {
//THREAD 线程池隔离策略 独立线程接收请求
@HystrixProperty(name = "execution.isolation.strategy", value = "THREAD "),
//时间单位设置
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000000000")
},
threadPoolKey = "queryContentshystrixJackpool", threadPoolProperties = {
@HystrixProperty(name = "coreSize", value = "10")//默认是10 可以更改
})
@Override
public List<ConsultContent> queryContents() {
log.info(Thread.currentThread().getName() + "========queryContents=========");
s.incrementAndGet();
List<ConsultContent> results = restTemplate.getForObject("http://"
+ SERVIER_NAME + "/user/queryContent", List.class);
return results;
}
怎么证明并发过程中创建新线程呢?创建并发测试类调用接口,基础代码如下:
@RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = MicroWebnoApplication.class) @WebAppConfiguration public class MyTest { private Logger logger = LoggerFactory.getLogger(getClass()); private Integer count = 11;//并发量为11 private CountDownLatch cdl = new CountDownLatch(count); @Autowired UserService userService; @Test public void hystrixTest() { for (Integer i = 0; i < count; i++) { new Thread(new Runnable() { @Override public void run() { try { cdl.await(); } catch (InterruptedException e) { e.printStackTrace(); } logger.info(Thread.currentThread().getName() + "==>" + userService.queryContents()); } }).start(); cdl.countDown(); } try { Thread.currentThread().join(); } catch (InterruptedException e) { e.printStackTrace(); } } }
所调客户端核心方法代码:
@RequestMapping("/queryContent") public List<ConsultContent> queryContent(HttpServletRequest request) { logger.info(""+this.hashCode()); logger.info("==================已经调用==========" + request.getRemotePort()); List<ConsultContent> results = new ArrayList<>(); ConsultContent c = new ConsultContent(); int i =0; c.setContent("查到对端数据了,ok!!!"+s); results.add(c); return results; }
2、启动客户端调用接口:结果如图所示:
用户线程和业务线程名不全一样,证明创建了新的线程,这种策略适合接口相应快的业务场景,请求速度快,占用内存资源大。
B:信号量隔离策略:
处理请求时不创建新的线程,用户线程和业务中的线程都是一样的,这种策略适用于请求时间稍长的业务接口,代码如下:
@HystrixCommand(fallbackMethod = "queryContentsFallback", //代表这个降级的方法,起到标识作用 commandKey = "queryContents", //groupKey 是线程池的名称,如果 threadPoolKey 没有配置,以groupKey 的值为准 groupKey = "querygroup-one", commandProperties = { //默认是10 可以更该的单位时间请求设置 @HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests",value = "10"), @HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE"), //时间单位设置 @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000000000") }, //threadPoolKey 的值是线程的名称,如果groupKey 也配置了,以threadPoolKey 的值为准; threadPoolKey = "queryContentshystrixJackpool", threadPoolProperties = { @HystrixProperty(name = "coreSize", value = "10")//默认是10 可以更改,如果上面已经自定义配置了,这里可以不配置 }) @Override public List<ConsultContent> queryContents() { log.info(Thread.currentThread().getName() + "========queryContents========="); s.incrementAndGet(); List<ConsultContent> results = restTemplate.getForObject("http://" + SERVIER_NAME + "/user/queryContent", List.class); return results; }
怎么证明并发过程中没有创建新线程呢?调用上面的并发测试类接口,效果如图:
线程名称是一样的,即证明,该策略占用内存资源相对小,有锁速度相对慢。注意:你发现两种隔离策略调接口时都是只调了10次(我们设置测试类的并发是11次,业务上设置10次),另外一处调用去哪了呢?超过请求上线,走了降级方法!!!图上有标注,可以看看。
二、服务降级:
异常的请求或者超过上限的请求走降级方法,而降级方法里面可以做具体的业务处理,也可以继续降级等业务。上面服务隔离策略里面已经包含了降级的部分内容。这里在梳理一下:
1、降级方法:
2、对应的具体业务方法: 降级和主业务方法的返回值要保持一样
public List<ConsultContent> queryContentsFallback() { f.incrementAndGet(); log.info("===============queryContentsFallback================降级走的方法="); return null; }
3、控制台打印 如图:
超过上限的方法走了降级方法。
三、数据监控:
Hystrix 进行服务熔断时会对调用结果进行统计,比如超时数、bad 请求数、降 级数、异常数等等都会有统计,
那么统计的数据就需要有一个界面来展示, hystrix-dashboard 就是这么一个展示 hystrix 统计结果的服务。
1、数据监控微服务搭建:jar包 引入
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
2、启动类配置:
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; /* * 监控界面:http://localhost:9990/hystrix * 需要监控的端点(使用了hystrix组件的端点):http://localhost:8083/actuator/hystrix.stream * * */ @SpringBootApplication @EnableHystrixDashboard //数据监控注解配置 public class HystrixDashboardApplication { public static void main(String[] args) { SpringApplication.run(HystrixDashboardApplication.class,args); } }
3、启动后访问:http://localhost:9990/hystrix 并填上信息
点击Monitor Strean 显示如图:
访问接口后显示:
这里数据监控演示完了!
四、熔断:
熔断就像家里的保险丝一样,家里的保险丝一旦断了,家里就没点了,家里用电 器功率高了就会导致保险丝端掉。
在我们 springcloud 领域也可以这样理解,如 果并发高了就可能触发 hystrix 的熔断。 熔断发生的三个必要条件,bootstrap.properties 配置如下:
#1、有一个统计的时间周期,滚动窗口 相应的配置属性 默认 10000 毫秒
metrics.rollingStats.timeInMilliseconds
#2、请求次数必须达到一定数量 相应的配置属性 默认 20 次
circuitBreaker.requestVolumeThreshold
#3、失败率达到默认失败率 相应的配置属性 默认 50%
circuitBreaker.errorThresholdPercentage
上述 3 个条件缺一不可,必须全部满足才能开启 hystrix 的熔断功能。 当我们的对一个线程池大小是 200 的方法压测时看看 hystrix 的熔断效果:
Jmeter 压测:

熔断器的三个状态:
1、关闭状态 关闭状态时用户请求是可以到达服务提供方的
2、开启状态 开启状态时用户请求是不能到达服务提供方的,直接会走降级方法
3、半开状态 当 hystrix 熔断器开启时,过一段时间后,熔断器就会由开启状态变成半开状态。
半开状态的熔断器是可以接受用户请求并把请求传递给服务提供方的,这时候如果远程调用 返回成功,
那么熔断器就会有半开状态变成关闭状态,反之,如果调用失败,熔断器就会有 半开状态变成开启状态。
Hystrix 功能建议在并发比较高的方法上使用,并不是所有方法都得使用的,切记。