SpringCloud中的Hystrix概述
服务雪崩
多个微服务之间调用的时候,A调用微服务B和微服务C,微服务B和微服务C调用了其他的微服务,这就是所谓的“扇出”。如果扇出的链路上某个微服务的调用响应的时间过长或者不可用,对微服务A的调用会占用越来越多的系统资源,进而引起系统崩溃,所谓的“雪崩效应”。
什么是Hystrix?
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多毅力啊不可避免的会调用失败,比如超时、异常等,Hystrix能够保障在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性
Hystrix可以用来干嘛?
- 服务降级
- 依赖隔离
- 服务熔断
官网地址:https://github.com/Netflix/Hystrix/wiki/How-To-Use
什么是服务降级?
降级机制时应对雪崩效应的一种微服务链路保护机制
当链路的某个微服务不可用或者响应时间太长,会进行服务的降级,进而降级微服务的调用,快速返回“错误”的信息。
优先核心服务,非核心服务不可用或者弱化
通过HystrixCommand注解指定
FallbackMethod(回退函数)中具体实现降级逻辑
- 案例:在pom文件中导入坐标
1 <dependencies> 2 <dependency> 3 <groupId>junit</groupId> 4 <artifactId>junit</artifactId> 5 </dependency> 6 <dependency> 7 <groupId>mysql</groupId> 8 <artifactId>mysql-connector-java</artifactId> 9 </dependency> 10 <dependency> 11 <groupId>com.alibaba</groupId> 12 <artifactId>druid</artifactId> 13 </dependency> 14 <dependency> 15 <groupId>ch.qos.logback</groupId> 16 <artifactId>logback-core</artifactId> 17 </dependency> 18 <dependency> 19 <groupId>org.mybatis.spring.boot</groupId> 20 <artifactId>mybatis-spring-boot-starter</artifactId> 21 </dependency> 22 23 <dependency> 24 <groupId>org.springframework.boot</groupId> 25 <artifactId>spring-boot-starter-web</artifactId> 26 </dependency> 27 <dependency> 28 <groupId>org.springframework.boot</groupId> 29 <artifactId>spring-boot-starter-test</artifactId> 30 </dependency> 31 32 <!-- 修改后立即生效,热部署 --> 33 <!-- https://mvnrepository.com/artifact/org.springframework/springloaded --> 34 <dependency> 35 <groupId>org.springframework</groupId> 36 <artifactId>springloaded</artifactId> 37 <version>1.2.8.RELEASE</version> 38 </dependency> 39 40 <dependency> 41 <groupId>org.springframework.boot</groupId> 42 <artifactId>spring-boot-devtools</artifactId> 43 </dependency> 44 <dependency> 45 <groupId>zh.stu.springcloud</groupId> 46 <artifactId>zhservicecloud-common</artifactId> 47 <version>1.0-SNAPSHOT</version> 48 </dependency> 49 <!--将微服务provider注册到eureka--> 50 <dependency> 51 <groupId>org.springframework.cloud</groupId> 52 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> 53 </dependency> 54 <dependency> 55 <groupId>org.springframework.cloud</groupId> 56 <artifactId>spring-cloud-starter-config</artifactId> 57 </dependency> 58 <!--监控完善信息--> 59 <dependency> 60 <groupId>org.springframework.boot</groupId> 61 <artifactId>spring-boot-starter-actuator</artifactId> 62 </dependency> 63 <dependency> 64 <groupId>org.springframework.cloud</groupId> 65 <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> 66 </dependency>67 68 </dependencies>
- 在yml的文件中添加配置
1 server: 2 port: 8001 3 mybatis: 4 config-location: classpath:mybatis/mybatis.cfg.xml #mybatis配置文件所在路径 5 type-aliases-package: com.zjx.springcloud.entity #所有Entity别名类所在包 6 mapper-locations: 7 - classpath:mybatis/mapper/**/*.xml #mapper映射文件 8 spring: 9 application: 10 name: txservicecloud-dep 11 datasource: 12 type: com.alibaba.druid.pool.DruidDataSource #当前数据源操作类型 13 driver-class-name: org.gjt.mm.mysql.Driver #mysql驱动包 14 url: jdbc:mysql://localhost:3306/cloudDB01 #数据库名称 15 username: root 16 password: root 17 dbcp2: 18 min-idle: 5 #数据库连接池的最小维持连接数 19 initial-size: 5 #初始化连接数 20 max-total: 5 #最大连接数 21 max-wait-millis: 200 #等待连接获取的最大超时时间 22 23 eureka: 24 client: #客户端注册进eureka服务列表内 25 service-url: 26 defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ 27 #defaultZone: http://localhost:7001/eureka 28 instance: 29 instance-id: txservicecloud-dep-hystrix-8001 #这个就是我用来做测试的模块的名字 30 prefer-ip-address: true 31 info: 32 app.name: txservicecloud-dep 33 company.name: www.txjava.cn 34 build.artifactId: @project.artifactId@ 35 build.version: @project.version@
- 可以编写一个查询数据库的一个案例,分不同的层,dao层,service....以及实现接口和xml文件。此处省略。直接在控制器中去模拟场景
- 例如在DeptController中模拟超时异常
1 /** 2 * @HystrixCommand 在注解中有一个 3 * fallbackMethod :在这个调用这个方法发生错误的时候会出发fallbackMethod中的方法,达到一个降级的效果。 4 * 5 * 6 * commandProperties : 是一个数组的形式,比如在里面可以配置超时的属性 7 * 里面的name的属性,可以在HysrixConmmandProperties.class文件中查找 8 * @param id 9 * @return 10 */ 11 12 @HystrixCommand(fallbackMethod = "HystrixProcess", 13 commandProperties = { 14 @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000") 15 }) 16 @RequestMapping(value = "/dept/get/{id}",method = RequestMethod.GET) 17 public Dept dept (@PathVariable("id") Long id){ 18 try { 19 // 这个方法表示,在线程休眠时间大于一秒的时候,该方法上的注解:@HystrixCommand会调用方法HystrixProcess中的提示信息。 20 Thread.sleep(2000); 21 } catch (InterruptedException e) { 22 e.printStackTrace(); 23 } 24 return deptService.findById(id); 25 }
因为在注解@HystrixCommand(fallbackMethod = "HystrixProcess",中调用HystrixProcess的方法,所以在添加一个HystrixProcess的方法。
1 /** 2 * 降级的服务的方法和返回值要和服务方法的方法一致 3 * @param id 4 * @return 5 */ 6 public Dept HystrixProcess(@PathVariable("id") Long id){ 7 Dept dept=new Dept(); 8 dept.setDeptno(id); 9 dept.setDname("该ID"+id+"没有对应的信息,null--@HystrixCommand"); 10 dept.setDb_source("访问超时"); 11 return dept; 12 13 }
这样在方法 dept 的这个方法的时候就会进入到 HystrixProcess 的这个方法中,提示异常的信息。
-
服务的超时我们也可以使用配置的方式来使用,在服务端的yml文件中
1 hystrix: 2 command: 3 default: 4 execution: 5 isolation: 6 thread : 7 timeoutInMilliseconds: 1000
注意:服务的降级方法的返回值和参数要和服务接口方法一致,默认降级方法可以设置参数列表为空。
@DefaultProperties(defaultFallback = "hystrixFallBack")可以通过@DefaultProperties来指定一个默认的服务降级方法,如果方法返回值统一(本例子统一了返回值的接口为String,默认的回退方法没有参数)则可以给多个服务接口来做服务降级。服务端的接口返回值修改,注意:服务消费端返回值也要修改成String。
服务熔断
什么是服务熔断?
当链路的某个微服务不可用或者响应时间太长,会进行服务的降级,进而熔断微服务的调用,快速返回"错误"的响应信息.当检测到该节点微服务调用响应正常后恢复调用链路.在SpringCloud框架里熔断机制通过Hystrix实现.Hystrix会监控微服务调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败就会启动熔断机制.熔断机制的注解是@HystrixCommand.
正常状态下,熔断器是关闭的一个状态,当出现调用失败、网络超时等就会处于打开的一种状态,当网络恢复之后就会处于半打开的一种状态。
circuitBreaker.enabled
熔断开启
circuitBreaker.requestVolumeThreshold
此属性设置时间窗口中请求断路的最小请求数。
例如,如果该值为20,那么如果在时间窗中仅接收19个请求(例如,10秒的时间窗),即使所有19个电路都失败,电路也不会跳闸。
circuitBreaker.sleepWindowInMilliseconds
熔断后的重试时间窗口,且在该时间窗口内只允许一次重试。即在熔断开关打开后,在该时间窗口允许有一次重试,如果重试成功,则将重置Health采样统计并闭合熔断开关实现快速恢复,否则熔断开关还是打开状态,执行快速失败。
circuitBreaker.errorThresholdPercentage
如果在一个采样时间窗口内,失败率超过该配置,则自动打开熔断开关实现降级处理,即快速失败。默认配置下采样周期为10s,失败率为50%。
在controller中访问方法是dept其中num的参数是2的就会在浏览器中提示服务器开小差的提示
@Autowired private DeptService deptService; @HystrixCommand(fallbackMethod = "getfallback" ,commandProperties = { @HystrixProperty(name = "circuitBreaker.enabled", value = "true"), @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"), @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"), @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60") }) @RequestMapping(value = "/dept/get/{id}/{num}",method = RequestMethod.GET) public Dept dept (@PathVariable("id") Long id,@PathVariable("num") int num){ if(num==2){ try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } return deptService.findById(id); } /** * 降级的服务的方法和返回值要和服务方法的方法一致 * @param id * @return */ public Dept HystrixProcess(@PathVariable("id") Long id,@PathVariable("num") int num){ Dept dept=new Dept(); dept.setDeptno(id); dept.setDname("服务开小差"); dept.setDb_source("服务开小差"); return dept; }
Feign中的Hystrix中的两种降级方式
第一种:
采用在接口调用服务的方式来做服务的熔断。
- 在子类的工程中的公共模块中添加一个接口
1 /** 2 * DeptClientServiceFallbackFactory:实现该接口的类 3 */ 4 @FeignClient(value = "zhservicecloud-dept",fallbackFactory = DeptClientServiceFallbackFactory.class) 5 public interface DeptClientService { 6 7 8 @RequestMapping(value = "/dept/add",method = RequestMethod.POST) 9 public boolean add(@RequestBody Dept dept); 10 11 @RequestMapping(value = "/dept/get/{id}",method = RequestMethod.GET) 12 public Dept getdept (@PathVariable("id") Long id); 13 14 @RequestMapping(value = "/dept/list",method = RequestMethod.GET) 15 public List<Dept> findAlldept(); 16 17 }
比如我是在对数据做的一系列操作模拟异常
- 在子类的工程中的公共模块中添加的一个类
1 /** 2 * 实现接口:FallbackFactory 3 * 4 * DeptClientService接口 5 */ 6 @Component 7 public class DeptClientServiceFallbackFactory implements FallbackFactory<DeptClientService> { 8 @Override 9 public DeptClientService create(Throwable throwable) { 10 return new DeptClientService() { 11 @Override 12 public boolean add(Dept dept) { 13 return false; 14 } 15 16 @Override 17 public Dept getdept(Long id) { 18 return null; 19 } 20 21 @Override 22 public List<Dept> findAlldept() { 23 System.out.println("Feign的第一种降级方式"); 24 return null; 25 } 26 }; 27 } 28 }
- 在模块Feign的消费端的yml文件中追加配置
1 feign: 2 hystrix: 3 enabled: true
注意:这是在服务的消费端,如果想要设置熔断的其他都需要在服务的消费端设置
- 在提供端的Controller中做测试
1 @RequestMapping(value = "/dept/list",method = RequestMethod.GET) 2 public List<Dept> dept() 3 { 4 try { 5 Thread.sleep(2000); 6 } catch (InterruptedException e) { 7 e.printStackTrace(); 8 } 9 return deptService.findAll(); 10 }
Feign的第二种降级方式
- 同样是在子模块下的common模块下新建一个接口
1 @FeignClient(value = "zhservicecloud-dept",fallbackFactory = DeptClientService1.DeptClientFallBack.class) 2 public interface DeptClientService1 { 3 4 @RequestMapping(value = "/dept/add",method = RequestMethod.POST) 5 public boolean add(@RequestBody Dept dept); 6 7 @RequestMapping(value = "/dept/get/{id}",method = RequestMethod.GET) 8 public Dept getdept(@PathVariable("id") Long id); 9 10 @RequestMapping(value = "/dept/list",method = RequestMethod.GET) 11 public List<Dept> findAlldept(); 12 13 // 创建一个内部类 14 @Component 15 static class DeptClientFallBack implements DeptClientService1{ 16 17 @Override 18 public boolean add(Dept dept) { 19 return false; 20 } 21 22 @Override 23 public Dept getdept(Long id) { 24 return null; 25 } 26 27 @Override 28 public List<Dept> findAlldept() { 29 System.out.println("Feign第二种降级方式"); 30 return null; 31 } 32 } 33 34 }
- 在Feign的消费端中的Controller测试
1 @RestController 2 public class DeptController1 { 3 4 5 @Autowired 6 private DeptClientService1 deptClientService; 7 8 @RequestMapping("/consumer1/dept/getdept/{id}") 9 public Dept getDept(@PathVariable("id") Long id){ 10 return deptClientService.getdept(id); 11 } 12 13 @RequestMapping("/consumer1/dept/findall") 14 public List<Dept> findAll(){ 15 return deptClientService.findAlldept(); 16 } 17 }