Hystrix断路器
已停更进维
多个微服务之间调用的时候,假设A调用BC,B、C有调用其他,越来越多,这就是所谓的“扇出”。如果扇出中某个微服务响应时间过长或不可用,对A的调用会占用越来越多的系统资源,进而引起系统崩溃。所谓的雪崩效应。
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
断路器本身是一种看管装置,当某个服务单元发生故障之后,通过断路器的故障键控,向调用方返回一个符合预期的、可处理的本宣响应(FallBack),而不是长时间的等待或者抛出调用方法无法处理的异常。
作用:
- 服务降级fallback
- 服务熔断break
- 接近实时的监控flowlimit
- 。。。
服务降级
大白话:对方系统不可用了,需要给一个兜底的解决方法。如服务忙,请稍后等待,不让客户端等待并立刻返回一个友好提示fallback
情况:
- 程序运行异常
- 超时
- 服务熔断触发
- 线程池/信号量打满也会导致服务降级
服务熔断
大白话:类似保险苏达到最大访问量后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示
服务限流
秒杀高并发等操作,进制一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行
Hystrix案例
7001改为单机
新建cloud-provider-hystrix-payment8001
写pom
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
写yml
server:
port: 8001
spring:
application:
name: cloud-provider-hystrix-payment
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka
instance:
instance-id: payment8001
prefer-ip-address: true
主启动
@SpringBootApplication
@EnableEurekaClient
public class PaymentHystrixMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.class,args);
}
}
service
@Service
public class PaymentService {
public String paymentInfo_OK(Integer id){
return "线程池:"+Thread.currentThread().getName()+" paymentInfo_OK,id: "+id;
}
public String paymentInfo_TimeOut(Integer id){
int timeNum=3000;
try {
Thread.sleep(timeNum);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "线程池:"+Thread.currentThread().getName()+" paymentInfo_TimeOut,id: "+id+" ;耗时"+(timeNum/1000)+"秒";
}
}
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;
}
}
若有两万个去访问timeout,访问ok也会变慢
新建一个80消费者cloud-consumer-feign-hystrix-order80
写pom
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
写yml
server:
port: 80
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://eureka7001.com:7001/eureka
主启动:
@SpringBootApplication
@EnableFeignClients
public class OrderHystrixMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderHystrixMain80.class,args);
}
}
service(feign)
@Component
@FeignClient(name = "CLOUD-PROVIDER-HYSTRIX-PAYMENT" )
public interface PaymentHystrixService {
@GetMapping("/payment/hystrix/ok/{id}")
String paymentInfo_OK(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")
String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}
controller
@RestController
@Slf4j
public class OrderHystrixController {
@Resource
private PaymentHystrixService paymentHystrixService;
@GetMapping("/consumer/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id){
return paymentHystrixService.paymentInfo_OK(id);
}
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
return paymentHystrixService.paymentInfo_TimeOut(id);
}
}
和之前一样,20000个timeout会使ok也变慢
超时、down机,都需有服务降级
降级配置
@HystrixCommand
8001从自身找问题:
设置自身调用超时时间的峰值,峰值内可以正常运行,超过了需要有兜底的方法处理,做服务降级fallback
8001service上配置,改了睡5s,但峰值为3s
@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3000")
})
public String paymentInfo_TimeOut(Integer id){
int timeNum=5000;
try {
Thread.sleep(timeNum);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "线程池:"+Thread.currentThread().getName()+" paymentInfo_TimeOut,id: "+id+" ;耗时"+(timeNum/1000)+"秒";
}
public String paymentInfo_TimeOutHandler(Integer id){
return "线程池:"+Thread.currentThread().getName()+" paymentInfo_TimeOutHandler,id: "+id+" ;不好意思,已超时!";
}
8001主启动类上,@EnableCircuitBreaker:
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class PaymentHystrixMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.class,args);
}
}
若报错如10/0也会直接到这个页面
不过降级一般更多在客户端
80订单微服务,也可以更好地保护自己,自己也依样画葫芦进行客户端降级保护
题外话:对@HystrixCommand内属性的修改建议重启微服务
yml中:
feign:
hystrix:
enabled: true
在主启动类上加:@EnableCircuitBreaker
业务类改controller
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "1500")
})
public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
return paymentHystrixService.paymentInfo_TimeOut(id);
}
public String paymentInfo_TimeOutHandler(@PathVariable("id") Integer id){
return "我是消费者80,对方支付系统繁忙,请稍后再试!!";
}
目前的问题
每个业务方法对应一个兜底的方法,代码膨胀,需要统一和自定义的分开
拿80举例:
在类上加:
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")
写一个全局方法
//下面是全局fallback
public String payment_Global_FallbackMethod(){
return "Global异常处理信息,请稍后再试!";
}
在需要降级的方法上加:@HystrixCommand
新问题:如果8001宕机,则会调用不到8001的方法
只需要为Feign客户端定义的接口添加一个服务降级处理的实现类即可实现解耦
写一个类继承用了feign的service接口
@Component
public class PaymentFallbackService implements PaymentHystrixService{
@Override
public String paymentInfo_OK(Integer id) {
return "-------PaymentFallbackService fall back,ok,o(╥﹏╥)o";
}
@Override
public String paymentInfo_TimeOut(Integer id) {
return "-------PaymentFallbackService fall back,timeout,o(╥﹏╥)o";
}
}
用了feign的service接口的注解上加上:fallback = PaymentFallbackService.class
指向上面的类
@FeignClient(name = "CLOUD-PROVIDER-HYSTRIX-PAYMENT" ,fallback = PaymentFallbackService.class)
关闭了8001在访问:
服务熔断
服务熔断,一开始降级操作,但当检测到该节点微服务调用响应正常后,恢复调用链路。缺省是5秒内20次调用失败,就会启动熔断机制。熔断机制的注解是@HystrixCommand
修改8001,service加上
@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不能是负数");
String serialNumber= IdUtil.simpleUUID();
return Thread.currentThread().getName()+"调用成功,流水号:"+serialNumber;
}
public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){
return "ID 不能为负数,请稍后再试,o(╥﹏╥)o id:"+id;
}
controller
@GetMapping("/payment/circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id){
String result = paymentService.paymentCircuitBreaker(id);
log.info("****result:"+result);
return result;
}
服务限流,后面到alibaba的Sentinel说明
服务监控hystrixDashboard
创建一个新的类:
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-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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
写yml
server:
port: 9001
主启动
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardMain9001 {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardMain9001.class,args);
}
}
所有Provider微服务提供类(8001、8002等)都需要监控依赖配置,pom中加
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
这样即配置成功
另外,因为版本原因,想监控8001,我们还需在
9001的yml中加入:
hystrix:
dashboard:
proxy-stream-allow-list: "*"
8001的主启动类上加入:
@Bean
public ServletRegistrationBean getServlet(){
HystrixMetricsStreamServlet streamServlet=new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean=new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
最终