Hystrix断路器的概述
分布式系统面临的问题
Hystrix是什么
Hystrix能干啥
1. 服务降级
2. 服务熔断
3. 接近实时的监控
Hystrix官网资料
https://github.com/Netflix/Hystrix/wiki/How-To-Use
Hystrix官宣,停更进维
Hystrix的重要概念
服务降级(fallback),服务熔断(break),服务限流(flowlimit)
在分布式系统中,服务与服务之间依赖错综复杂,一种不可避免的情况就是某些服务将会出现失败。Hystrix是一个用于处理分布式系统的延时和容错的开源库,它提供了服务与服务之间的容错功能,主要体现在延迟容错和容错,从而做到控制分布式系统中的联动故障。Hystrix通过隔离服务的访问点,阻止联动故障,并提供故障的解决方案,从而提高了这个分布式系统的弹性。
服务降级
“断路器”本身就是一种开关装置。当某个服务单元发生故障后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的,可处理的备选响应(fallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就可以保证了服务调用方的线程不会被长时间,不必要地占用,从而避免了故障在分布式系统中的蔓延,
乃至雪崩。
哪些情况会出现降级?
l 程序运行异常
l 超时
l 服务熔断出发服务降级
l 线程池/信号量打满也会导致服务降级
服务熔断
类似保险丝达到最大服务访问后,直接拒接访问。然后调用服务降级的方法并返回友好提示。
就是保险丝。服务的降级-》进而熔断-》恢复调用链路
服务限流
秒杀高并发等操作,严禁一窝峰的过来拥挤,大家排队,一秒钟N个,有序进行
Hystrix案例
Hystrix支付微服务构建
构建工程
1. 新建cloud-provider-hystrix-payment8001工程
2. 添加pom
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.fengyangcai.springcloud</groupId> <artifactId>cloud-api-commons</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> </dependencies>
3. 写yml
server:
port: 8001
spring:
application:
name: cloud-provider-hystrix-payment
eureka:
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://localhost:7003/eureka
#defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka #集群版本
4. 主启动类
@SpringBootApplication @EnableEurekaClient //@EnableDiscoveryClient //@EnableCircuitBreaker//断路器的注解 public class HystrixPaymentMain8001 { public static void main(String[] args) { SpringApplication.run(HystrixPaymentMain8001.class,args); } }
5. 业务类
Service层
@Service public class PaymentService { //正常的访问方法 public String paymentInfo_OK(Integer id){ return "线程池:"+Thread.currentThread().getName()+"paymentInfo_OK,id: "+id+"/t"+"哈哈"; } //一般Hstrix降级放在客户端,这里放在服务端了只是演示而已。 /* @HystrixCommand(fallbackMethod = "payment_TimeOutHandler",commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000") })*/ public String paymentInfo_TimeOut(Integer id){ int time=1; // int a=10/0; try { TimeUnit.SECONDS.sleep(time); }catch (InterruptedException e){ e.printStackTrace(); } return "线程池:"+Thread.currentThread().getName()+"--paymentInfo_TimeOut,id: "+id+"/t"+"哈哈耗时:"+time+"秒"; } public String payment_TimeOutHandler(Integer id){ return "线程池:"+Thread.currentThread().getName()+"--paymentInfo_TimeOut,id: "+id+"/t"+"####系统繁忙呜呜呜##"; } }
Controller层
@RestController @Slf4j public class PaymentController { @Resource private PaymentService paymentService; @Value("${server.port}") private String serverPort; @GetMapping("/payment/hystrix/ok/{id}") public String paymentInfo_OK(@PathVariable("id")Integer id){ String result = paymentService.paymentInfo_OK(id); log.info("&&&&&&result"+result); return result; } @GetMapping("/payment/hystrix/timeout/{id}") public String paymentInfo_timeout(@PathVariable("id")Integer id){ String result = paymentService.paymentInfo_TimeOut(id); log.info("*******result"+result); return result; } }
6. 测试
出现情况:
Tomcat的默认的工作线程数被打满了,没有多余的线程来分解压力和处理。出现两个都在自己转圈圈。
上述结论
如何解决?解决的要求
创建cloud-consumer-feign-hystrix-order80消费端
Pom
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>com.fengyangcai.springcloud</groupId> <artifactId>cloud-api-commons</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
Yml
server:
port: 80
spring:
application:
name: cloud-consumer-feign-hystrix-order
eureka:
client:
register-with-eureka: false #表示是否将自己注册进EurekaServer默认为true
fetch-registry: true #是否从EurekaServer捉取已有的注册信息,默认为true.单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
service-url:
#defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka
defaultZone: http://localhost:7003/eureka
主函数
其中@EnableCircuitBreaker就是引入了hystrix的熔断注解标签
@SpringBootApplication @EnableFeignClients @EnableCircuitBreaker public class FeignHystrixOrderMain80 { public static void main(String[] args) { SpringApplication.run(FeignHystrixOrderMain80.class,args); } }
使用openFeign在service层调用服务
@Component @FeignClient(value = "cloud-provider-hystrix-payment") public interface PaymentFeignHystrixService { @GetMapping("/consumer/payment/hystrix/ok/{id}") public String paymentInfo_OK(@PathVariable("id")Integer id); @GetMapping("/consumer/payment/hystrix/timeout/{id}") public String paymentInfo_timeout(@PathVariable("id")Integer id); }
Controller层
其中@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")是全局的一个处理方法标签。
@HystrixCommand是hystrix的一个处理标签。
@RestController @Slf4j @DefaultProperties(defaultFallback = "payment_Global_FallbackMethod") public class OrderFeignHystrixController { @Resource private PaymentFeignHystrixService paymentFeignHystrixService; @GetMapping("/payment/hystrix/ok/{id}") public String paymentInfo_OK(@PathVariable("id")Integer id){ return paymentFeignHystrixService.paymentInfo_OK(id); } //一般Hstrix降级放在客户端 @GetMapping("/payment/hystrix/timeout/{id}") /* @HystrixCommand(fallbackMethod = "payment_TimeOutHandler",commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1500") })*/ @HystrixCommand public String paymentInfo_timeout(@PathVariable("id")Integer id){ return paymentFeignHystrixService.paymentInfo_timeout(id); } public String payment_TimeOutHandler(Integer id){ return "我是消费方80.对方支付系统繁忙请稍后访问"; } //全局fallback方法 public String payment_Global_FallbackMethod(){ return "全局的降级处理方法"; } }
上面是使用了服务降级的配置
@HystrixCommand
在官网上是程序,工程落地都是使用配置方式
@HystrixCommand(fallbackMethod = "payment_TimeOutHandler",commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1500")})
中,fallbackMethod是回路的执行方法,1500代表1.5秒,执行超过这个时间就执行回路方法。
总结一下使用
引入依赖,在yml文件中配置
feign:
hystrix:
enabled: true
在启动类上加上
@EnableHystrix,如果是熔断加@EnableCircuitBreaker
业务类方法上加 @HystrixCommand(fallbackMethod = "payment_TimeOutHandler",commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1500")})
这个导致代码膨胀,每个业务方法对应了一个兜底的方法。我们要统一和自定义的分开。
在feign的接口上配置
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")其中 payment_Global_FallbackMethod就是兜底的方法。
这样还是把处理的兜底方法放在同一个类啊。
下面我们解决这个问题
为Feign客户端定义的接口添加一个服务降级处理的实现类
@Component public class PaymentFallbackService implements PaymentFeignHystrixService{ @Override public String paymentInfo_OK(Integer id) { return "-----PaymentFallbackService fall back"; } @Override public String paymentInfo_timeout(Integer id) { return "-----PaymentFallbackService fall back-paymentInfo_timeout"; } }
在yml上要加上
Hystrix之服务熔断理论
大师的论文:https://martinfowler.com/bliki/CircuitBreaker.html
实操
修改我们上面的服务提供工程cloud-provider-hystrix-payment8001
配置PaymentService
//======服务熔断 @HystrixCommand(fallbackMethod ="paymentCircuitBreaker_fallback",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"),//失败率达到多少后跳闸 }) public String paymentCircuitBreaker(@PathVariable("id")Integer id){ if (id<0){ throw new RuntimeException("***********id 不能为负数"); } //使用hutool工具吧创建一个没有-的字符串 String serialNumber = IdUtil.simpleUUID(); return Thread.currentThread().getName()+" "+"调用成功,流水号为: "+serialNumber; } public String paymentCircuitBreaker_fallback(@PathVariable("id")Integer id){ return "id 不能为负数,请稍后再试,--paymentCircuitBreaker_fallback"; }
why配置这些参数?
intellj idea 使用ctrl+N的查看HystrixCommandProperties 类,这个类记载了HystrixCommand的全部属性
在controller层
//====服务熔断 @GetMapping("/payment/hystrix/paymentCircuitBreaker") public String paymentCircuitBreaker(@PathVariable("id")Integer id){ String result = paymentService.paymentCircuitBreaker_fallback(id); log.info("****result:"+result); return result; }
测试
结论
官网断路器流程图
断路器在什么情况下开始起作用?
断路器打开之后
所有的配置
服务限流
参照alibaba的sentinel说明
hystrix的工作流程
https://github.com/Netflix/Hystrix/wiki/How-it-Works
官网图例
网上找
服务监控hystrixDashboard
概述
仪表盘9001
新建cloud-consumer-hystrix-dashboard9001
pom
<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-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
yml
server:
port: 9001
hystrixDashboardMain9001 +新的注解@EnableHystrixDashboard
@SpringBootApplication @EnableHystrixDashboard public class HystrixDashboardMain9001 { public static void main(String[] args) { SpringApplication.run(HystrixDashboardMain9001.class,args); } }
所有Provider微服务提供类(8001/8002/8003)都需要监控依赖配置
启动cloud-consumer-hystrix-dashboard9001该微服务后续将监控微服务8001
http://localhost:9001/hystrix
要在监控的主启动类上配置
使用观察监控窗口
填写监控地址http://localhost:8001/hystrix.stream
测试地址