一、Hystrix
1、概念
Hystrix是一个用于处理分布式系统的延迟和容错的开源库。能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。发生故障后,向调用方返回一个符合预期的、可处理的备选响应(Fallback),而不是长时间的等待或者抛出调用方无法处理的异常。
- 服务降级(Fallback):服务器忙,请稍后再试,不让客户端等待并立刻返回一个友好提示。出现降级的情况:程序运行异常、超时、服务熔断触发服务降级、线程池/信号量打满也会导致服务降级。
- 服务熔断(break):类似保险丝达到最大服务访问后,直接拒绝访问,拉闸限电。然后调用服务降级的方法并返回友好提示。
- 服务限流(flowlimit):秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒种N个,有序进行
2、服务降级fallback(可用在提供方/消费方)
- 服务方
-pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
- service/paymentService
@Service
public class PaymentService {
//正常访问
public String paymentInfo_ok(Integer id) {
return "线程池:" + Thread.currentThread().getName() + ",paymentInfo_ok,id=" + id;
}
//停5秒访问
//兜底处理。一旦调用服务方法失败并抛出错误信息后,会自动调用@HystrixCommand标注好的fallbackMethod调用类中的指定方法(paymentInfo_handler)。
@HystrixCommand(fallbackMethod = "paymentInfo_handler",commandProperties = {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="3000")
})
public String paymentInfo_timeout(Integer id) {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "线程池:" + Thread.currentThread().getName() + ",paymentInfo_timeout,id=" + id;
}
public String paymentInfo_handler(Integer id) {
return "线程池:" + Thread.currentThread().getName() + "服务器繁忙,请稍后再试,id="+id ;
}
}
- 启动类中添加@EnableCircuitBreaker
当前服务不可用了,做服务降级,兜底的方案都是paymentInfo_handler
- 消费方
- application.yml
feign:
hystrix:
enabled: true
- 启动类上加@EnableHystrix
- controller上
@RestController
public class OrderController {
@Resource
private PaymentFeignServer paymentFeignServer;
//访问超时
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
@HystrixCommand(fallbackMethod = "consumerOrder_handler",commandProperties = {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="1500")
})
public String paymentInfo_timeout(@PathVariable("id") Integer id) {
return paymentFeignServer.paymentInfo_timeout(id);
}
public String consumerOrder_handler(Integer id) {
return "消费方80,线程池:" + Thread.currentThread().getName() + "服务器繁忙,请稍后再试,id="+id ;
}
}
- 解决代码耦合度高的问题(业务逻辑与fallback代码混在一起)
- application.yml
feign:
hystrix:
enabled: true #在feign中开启hystrix
- service
@Component
//从CLOUD-PROVIDER-PAYMENTHYSTIX中查找,若出现问题,则进行服务降级,从PaymentFallbackService中找。
@FeignClient(value = "CLOUD-PROVIDER-PAYMENTHYSTIX",fallback = PaymentFallbackService.class)
public interface PaymentFeignServer {
@GetMapping("/payment/hystrix/ok/{id}")
String paymentInfo_ok(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")
String paymentInfo_timeout(@PathVariable("id") Integer id);
}
@Component
public class PaymentFallbackService implements PaymentFeignServer {
@Override
public String paymentInfo_ok(Integer id) {
return "ok服务降级处理";
}
@Override
public String paymentInfo_timeout(Integer id) {
return "timeout服务降级处理";
}
}
- 代码膨胀问题
@RestController
//若方法上有注解@HystrixCommand,并且没有fallbackMethod属性,则出问题默认调用payment_global_fallbackMethod降级方法
@DefaultProperties(defaultFallback = "payment_global_fallbackMethod")
public class OrderController {
@Resource
private PaymentFeignServer paymentFeignServer;
//访问超时
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
@HystrixCommand
public String paymentInfo_timeout(@PathVariable("id") Integer id) {
return paymentFeignServer.paymentInfo_timeout(id);
}
public String payment_global_fallbackMethod() {
return "全局fallback降级处理";
}
}
3、服务熔断
- 提供方
- service
@Service
public class 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"),//失败率达到过少后跳闸
//在10秒内,10次请求次数中失败率达到6次,就会跳闸。出现访问正常也会跳到降级处理的方法中,默认5s后恢复正常
})
public String paymentCircuitBreaker(Integer id) {
if (id < 0) {
throw new RuntimeException("id不能为负数");
}
String s = IdUtil.simpleUUID();
return Thread.currentThread().getName() + "\t" + "流水号:"+ s;
}
public String paymentCircuitBreaker_fallback(Integer id) {
return "id不能为负数,请稍后重试";
}
}
- controller
@RestController
public class PaymentController {
@Resource
private PaymentService paymentService;
@GetMapping("/payment/circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id){
return paymentService.paymentCircuitBreaker(id);
}
}
运行结果
刚开始访问:
访问负数:
负数访问次数过多:
- 熔断类型
1、熔断打开:请求不再进行调用当前服务,内部设置时钟一般为MTTR(平均故障处理时间),当打开时长达到所设时钟则进入半熔断状态。
2、熔断关闭:熔断关闭不会对服务进行熔断
3、熔断半开:部分请求根据规则调用当前服务,如果请求成功且符合规则则认为当前服务恢复正常,关闭熔断。
涉及到断路器的三个参数:快照时间窗(sleepWindowInMilliseconds),请求总数阀值(requestVolumeThreshold),错误百分比阀值(errorThresholdPercentage)
4、hystrix的图形化Dashboard监控
- 监控方
- pom.xml <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
- application.yml
server:
port: 9001
- 启动类添加@EnableHystrixDashboard
启动成功
- 被监控方
- pom.xml
<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>
- 启动类
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class PaymenyHystrixMain8081 {
public static void main(String[] args) {
SpringApplication.run(PaymenyHystrixMain8081.class,args);
}
//此配置是为了服务监控而配置,与服务容错本身无关。
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(streamServlet);
servletRegistrationBean.setLoadOnStartup(1);
servletRegistrationBean.addUrlMappings("/hystrix.stream");
servletRegistrationBean.setName("HystrixMetricsStreamServlet");
return servletRegistrationBean;
}
}
测试
访问:http://localhost:8081/payment/circuit/1
断路器未打开
访问:http://localhost:8081/payment/circuit/-1
断路器打开