转载请注明出处:https://blog.csdn.net/weixin_41133233/article/details/85070323
本文出自 程熙cjp 的博客
继上一篇讲述完ribbon的负载均衡和源码追踪之后,本篇小熙将会讲述Hystrix的详解和实例。
一. Hystrix介绍
在分布式系统,我们一定会依赖各种服务,那么这些个服务一定会出现失败的情况,Hystrix就是这样的一个工具,它通过提供了逻辑上延时和错误容忍的解决力来协助我们完成分布式系统的交互。Hystrix 通过分离服务的调用点,阻止错误在各个系统的传播,并且提供了错误回调机制,这一系列的措施提高了系统的整体服务弹性。
二. 配置案例环境
-
pom文件导入坐标
<!--引入Hystrix依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
-
开启SpringBoot启动类的支持
-
在yml配置文件中配置hystrix
server: port: 8094 spring: application: # 注册服务的名称,注意服务器名称不能使用下划线连接,可以使用中划线连接 name: user-consumer cloud: # 开启SpringCloud负载均衡中的重试功能,ribbon中整合了(默认是重试到熔断机制的时间),但是zuul整合时需要指定 loadbalancer: retry: enabled: true # 开启Spring Cloud的重试功能 user-service: # 注意这是提供某一服务系统的服务名称,仅对这一服务起作用,不能随意抒写 ribbon: # NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 修改ribbon中默认的轮询为随机(采用spring系统提供包) ConnectTimeout: 250 # Ribbon的连接超时时间 ReadTimeout: 1000 # Ribbon的数据读取超时时间 OkToRetryOnAllOperations: true # 是否对所有操作都进行重试 MaxAutoRetriesNextServer: 1 # 切换实例的重试次数 MaxAutoRetries: 1 # 对当前实例的重试次数 eureka: client: # 注册eureka的地址 service-url: defaultZone: http://127.0.0.1:8090/eureka,http://127.0.0.1:8091/eureka,http://127.0.0.1:8092/eureka instance: # 当其它服务获取地址时提供ip而不是hostname prefer-ip-address: true # 指定自己的ip信息,不指定的话会自己寻找 ip-address: 127.0.0.1 # 设置hystrix的超时时间为6000ms,因为ribbon默认的重试时间和熔断机制的默认时间都是1000毫秒,所以先触发熔断机制。 # 为了让重试机制生效所以熔断机制的默认时间一定要比ribbon的重试时间长 hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 6000
-
在消费者的Service层添加UserHystrixService 类,被Controller层调用:
package com.chengxi.service; import com.chengxi.pojo.TUser; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; /** * @author chengxi * @date 2018/12/4 20:44 */ @Service @Slf4j // 日志类注解 //@Transactional public class UserHystrixService { @Autowired private RestTemplate restTemplate; /** * 导入日志类,被idea的slf4j注解取代 */ // private static final Logger logger = LoggerFactory.getLogger(UserHystrixService.class); /** * 演示熔断机制,注意要在服务器处设置线程睡眠用来显示请求超时,熔断机制接收到请求超时的时候回调用失败回滚函数 * fallbackMethod 表示要调用的失败回调函数 * @param id * @return */ @HystrixCommand(fallbackMethod = "selectUserByIdFallback") public TUser selectUsersById(Integer id){ // 开始发起请求的时间 long begin = System.currentTimeMillis(); // 拼接发送的url地址 String url = "http://user-service/userService/"+id; // 根据整合的restTemplate模板发送请求,并添加到用户集合中 ResponseEntity<TUser> user = restTemplate.getForEntity(url, TUser.class); // 响应结束的时间 long end = System.currentTimeMillis(); // 使用idea提供的slf4j输出日志,当前类直接输出不用再标记。大括号是用来存放后面的数值的 log.info("访问的时间是:{}",end - begin); // 使用日志记录访问的时间 // logger.info("访问的时间是:{}",end - begin); return user.getBody(); } /** * Hystrix失败回滚调用的函数 * @param id * @return */ public TUser selectUserByIdFallback(Integer id){ TUser user = new TUser(); user.setId(id); user.setUsername("用户查询的信息出现异常"); return user; } }
-
继续在消费者的Controller层的UserController类中添加hystrix测试的方法:
@Autowired private UserHystrixService userHystrixService; /** * 调用service中写的熔断机制代码 * @param ids * @return */ @GetMapping(value = "/selectUsersAndHystrix") public List<TUser> selectUser(@RequestParam ArrayList<Integer> ids){ ArrayList<TUser> tUsers = new ArrayList<>(); // 使用lambda表达式简写返回用户的循环 ids.forEach(id -> { TUser user = userHystrixService.selectUsersById(id); tUsers.add(user); }); return tUsers; }
-
在服务提供端UserController类中添加线程休眠代码,用于测试熔断机制
package com.chengxi.controller; import com.chengxi.pojo.TUser; import com.chengxi.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Random; /** * @author chengxi * @date 2018/12/3 09:28 */ @RestController @RequestMapping(value = "/userService") public class UserController { @Autowired private UserService userServiceImpl; @GetMapping(value = "/{id}") public TUser selectUserById(@PathVariable Integer id) throws InterruptedException { // 为了演示hystrix熔断机制,这里设置返回超长时间,让其调用失败回滚函数 // (默认是超过1000毫秒执行,最好重新设置因为重试机制的时间也是1000毫秒,且熔断机制的优先级高于重试机制,所以熔断机制的值要设置比重试机制高) int longTime = new Random().nextInt(8000); Thread.sleep(longTime); System.out.println("longTime:"+longTime); return userServiceImpl.selectUserById(id); } }
-
解释为什么不用导入重试机制的坐标
由图可以看出,Hystrix坐标已经整合了Ribbon和他的重试机制
三. 测试运行
访问消费者中的hystrix测试方法:
-
结果如图:
如上图,由于yml文件中配置的熔断时间是6000毫秒,但是线程睡眠了7690毫秒,所以由于请求超时,被熔断机制判定为请求的服务宕机了,中断请求并返回失败回调函数的结果。
-
解释
注意这里小熙只是模拟线程睡眠,以来达到超时被熔断机制中断请求。而没有具体演示请求失败的重试机制,因为这只是一个简单案例没有涉及到服务端出现对应异常,现在就来好好说说吧。
还记得我们在yml配置文件中配置的hystrix的配置吗:
MaxAutoRetriesNextServer: 1 # 切换实例的重试次数 MaxAutoRetries: 1 # 对当前实例的重试次数
这里的流程应该是这样的:(请求的时间是按照累加的,而不是单个次数的)
(1)首先消费者向服务端发送请求
(2)接着如果请求失败但是请求的时间没有超过熔断的时间,则会按照yml文件中配置的请求实例的次数继续重试,如果成功则停止,下面有几种情况:
1. 未成功且还未超过重试实例次数且未超过熔断时间则继续请求, 2. 如果次数已经达到了,未超过熔断时间,就看是否有配置切换另一个实例的次数,如果有就切换实例进行请求,没有就等待熔断时间被中断 3. 切换另一个实例步骤如上(注意一切都要关注熔断时间,时间一到未成功就中断)
-
注意
这里经过小熙的测试,如果不配置实例的重试次数和切换实例的次数,则按照一直请求直到成功或者一直请求到熔断时间中断,也就是说重试次数无上限。
嗯,至此小熙关于hystrix熔断机制的讲解就暂告一段落了,下一篇小熙将讲解Feign的服务。