目录
Hystrix 全局服务降级Default Properties
简介
Hystrix功能:服务降级,服务熔断,接近实时的监控,限流,隔离等。
官网:https://github.com/Netflix/Hystrix/wiki/How-To-Use
Hystrix停更进维
Hystrix重要概念
服务降级(fallback)
-简单来说就是 假如对方系统不可用了,你需要给我一个兜底的解决方法,不要让我在那耗死
服务熔断(break)
服务限流(flowlimit)
Hystrix案例
Hystrix支付微服务构建
把7001改为单机版,方便后面进行案例测试。
1.新建cloud-provider-hystrix-payment8001
2.写pom
<dependencies>
<!--新增hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.aurora.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
3.改yml
server:
port: 8001
eureka:
client:
register-with-eureka: true #表识不向注册中心注册自己
fetch-registry: true #表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
service-url:
#defaultZone: http://eureka7002.com:7002/eureka/ #设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址
defaultZone: http://eureka7001.com:7001/eureka/
# server:
# enable-self-preservation: false
spring:
application:
name: cloud-provider-hystrix-payment
# eviction-interval-timer-in-ms: 2000
4.主启动
@SpringBootApplication
@EnableEurekaClient
public class PaymentHystrixMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.class,args);
}
}
5.service
@Service
public class PaymentService {
//成功
public String paymentInfo_OK(Integer id){
return "线程池:"+Thread.currentThread().getName()+" paymentInfo_OK,id: "+id+"\t"+"哈哈哈" ;
}
//失败
public String paymentInfo_TimeOut(Integer id){
int timeNumber = 3;
try { TimeUnit.SECONDS.sleep(timeNumber); }catch (Exception e) {e.printStackTrace();}
return "线程池:"+Thread.currentThread().getName()+" paymentInfo_TimeOut,id: "+id+"\t"+"呜呜呜"+" 耗时(秒)"+timeNumber;
}
}
6.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;
}
}
7.测试
启动eureka7001 再启动cloud-provider-hystrix-payment8001
访问 success的方法 http://localhost:8001/payment/hystrix/ok/31
每次调用耗费3秒钟 http://localhost:8001/payment/hystrix/timeout/31
上述module均OK 以上述为根基平台,从正确->错误->降级熔断->恢复
高并发测试
安装JMeter
JMeter下载地址:http://jmeter.apache.org/download_jmeter.cgi
下载tgz和zip都可以:
进入解压后的目录的bin目录,找到jmeter.properties文件,修改语言zh_CN。
双击jmeter.bat运行jmeter,修改语言成中文版。
Jmeter压测测试
进行高并发测试 添加测试计划 线程组
保存当前线程组
添加http请求
设置如下:
保存后启动
然后去访问http://localhost:8001/payment/hystrix/ok/31,访问速度变慢了。两个都在转圈圈
而后台一直在发送超时结果
导致原因:
由于这两个方法在同一个微服务里面 大规模的资源打到timeout 微服务不得不集中资源去处理这些高并发请求 (武汉疫情严重 全国各地都去支援 同理)
tomcat的默认的工作线程数被打满了,没有多余的线程来分解压力和处理。
Jmeter压测结论
上面还是服务提供者8001自己测试,假如此时外部的消费者80也来访问,那消费者只能干等,最终导致消费端80不满意,服务端8001直接被拖死
新建80加入
1.新建cloud-consumer-feign-hystrix-order80
2.改pom
<dependencies>
<!--新增hystrix-->
<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>com.aurora.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
3.写yml
server:
port: 80
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://localhost:7001/eureka
#需要加上,否则会报错
ribbon:
ReadTimeout: 4000
ConnectTimeout: 4000
4.主启动
@SpringBootApplication
@EnableFeignClients //激活feign
public class OrderHystrixMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderHystrixMain80.class,args);
}
}
5.service
@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT") //微服务名称 80要调8001
public interface PaymentHystrixService {
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}
6.controller
@RestController
@Slf4j
public class OrderHystrixController {
@Resource
private PaymentHystrixService paymentHystrixService;
@Value("${server.port}")
private String serverPort;
@GetMapping("/consumer/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id){
String result = paymentHystrixService.paymentInfo_OK(id);
log.info("*******result:"+result);
return result;
}
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
String result = paymentHystrixService.paymentInfo_TimeOut(id);
log.info("*******result:"+result);
return result;
}
}
7.测试
启动hystrix-order80 浏览器:http://localhost/consumer/payment/hystrix/ok/31
8.启动jmeter,然后再进行测试
故障现象、导致原因
现象:
8001同一层次的其他接口服务被困死,因为tomcat线程里面的工作线程已经被挤占完毕
80此时调用8001,客户端访问响应缓慢,转圈圈
上诉结论:正因为有上述故障或不佳表现,才有我们的降级/容错/限流等技术诞生
降级容错解决的维度要求
服务降级
8001fallback 提供者
1.修改8001中PaymentService的paymentInfo_TimeOut方法,并添paymentInfo_TimeOutHandler方法:
@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler", commandProperties = {
//设置自身超时调用时间的峰值为3秒,峰值内可以正常运行,超过了需要有兜底的方法处理,服务降级fallback
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
})
public String paymentInfo_TimeOut(Integer id){
int timeNumber = 5;
//int i = 1 / 0;
try {
TimeUnit.SECONDS.sleep(timeNumber);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "线程池:" + Thread.currentThread().getName() +
"\tpaymentInfo_TimeOut,id:" + id + ",耗时:" + timeNumber + "秒";
}
public String paymentInfo_TimeOutHandler(Integer id){
return "8001提供者,线程池:" + Thread.currentThread().getName() +
"\tpaymentInfo_TimeOutHandler系统繁忙,请稍后再试,id:" + id;
}
2.然后在8001的主启动类上添加@EnableCircuitBreaker
注解,启用断路器。
3.启动7001和8001,测试8001的fallback,http://localhost:8001/payment/hystrix/timeout/1
成功进入fallback方法。(并且fallback方法是用Hystrix的线程池)
4.去掉sleep,改为 1 / 0,测试方法运行异常 localhost:8001/payment/hystrix/timeout/1,也可以进入fallback方法。
80fallback 客户端 消费者
1.改yml
feign:
hystrix:
enabled: true #如果处理自身的容错就开启。开启方式与生产端不一样。
2.在主启动类添加@EnableHystrix
注解。
3.业务类
修改OrderHystrixController的paymentInfo_TimeOut方法,并添加paymentTimeOutFallbackMethod方法
@HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")
})
public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
String result = paymentHystrixService.paymentInfo_TimeOut(id);
return result;
}
public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id){
return "消费者80,对方支付系统繁忙请10秒钟后再试或自己运行出错检查自己,/(ㄒoㄒ)/~";
}
在80 controller 中添加int i = 1 / 0;
,运行异常也会进入80的fallback方法
目前的问题和解决办法
解决办法:设置全局fallback方法。
Hystrix 全局服务降级Default Properties
@DefaultProperties(defaultFallback = "")
1.在80的OrderHystrixController中添加全局fallback方法:
//全局fallback方法,不能有传参
public String payment_Global_FallbackMethod(){
return "Global异常处理信息,请稍后再试!";
}
2.在OrderHystrixController类上加上@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod") ,设置全局fallback方法。
3.把paymentInfo_TimeOut方法的@HystrixCommand改成:
4.测试
重启80 localhost/consumer/payment/hystrix/timeout/1 显示Global异常
Hystrix 通配服务降级Feignfallback
模拟宕机场景
1.在80的service包下新建PaymentFallbackService类,实现PaymentHystrixService接口
@Component
public class PaymentFallbackService implements PaymentHystrixService {
@Override
public String paymentInfo_OK(Integer id) {
return "-----PaymentFallbackService fall back-paymentInfo_OK , (┬_┬)";
}
@Override
public String paymentInfo_TimeOut(Integer id) {
return "-----PaymentFallbackService fall back-paymentInfo_TimeOut , (┬_┬)";
}
}
2.要在yml中加上:(我们在之前就加上了)
feign:
hystrix:
enabled: true
3.给PaymentHystrixService接口的@FeignClient注解加上fallback = PaymentFallbackService.class
属性,用于出错进行fallback处理。
4.测试 先启动8001 再启动80
正常访问测试:http://localhost/consumer/payment/hystrix/ok/31
故意关闭微服务8001 调用PaymentFallbackService中的paymentInfo_ok方法
服务熔断
断路器:就是保险丝 https://martinfowler.com/bliki/CircuitBreaker.html
熔断概念
实操
1.修改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 不能负数");
}
String serialNumber = IdUtil.simpleUUID(); //等价于UUID.randomUUID().toString()
return Thread.currentThread().getName()+"\t"+"调用成功,流水号:"+serialNumber;
}
public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){
return "id 不能负数,请稍候再试,(┬_┬)/~~ id: " +id;
}
IdUtil.simpleUUID() 来自hutool中的 唯一ID工具-IdUtil (hutool.cn)
为什么配置这些参数
2.在8001的PaymentController中添加
//===服务熔断
@GetMapping("/payment/circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id){
String result = paymentService.paymentCircuitBreaker(id);
log.info("*******result:"+result);
return result;
}
3.测试 启动7001和8001
正确:http://localhost:8001/payment/circuit/31
错误:http://localhost:8001/payment/circuit/-31 (输入超过10次以上 错误率六成进入熔断)
再尝试多次等正确率上升才恢复
总结
如果请求次数的错误率超过指定值,开启熔断,经过一段时间后,变为半开模式,然后放进一个请求进行处理,如果请求处理成功,关闭熔断;如果还是报错,继续进入熔断,再经过一段时间后,变为半开模式,再进行对下一个请求进行处理,一直在熔断,半开模式来回切换,直到请求成功,关闭熔断。
熔断类型
官网熔断流程图
断路器在什么情况下开始起作用:https://github.com/Netflix/Hystrix/wiki/How-it-Works
断路器开启或关闭的条件:
断路器打开之后:
All配置
服务限流
会在后面高级篇alibaba的Sentinel讲解。
Hystrix工作流程
https://github.com/Netflix/Hystrix/wiki/How-it-Works
官方图例:
服务监控HystrixDashboard
简介
仪表盘9001
1.新建cloud-consumer-hystrix-dashboard9001
2.改pom
<dependencies>
<!-- hystrix仪表盘图形化 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>com.aurora.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
3.写yml
server:
port: 9001
4.主启动 ( HystrixDashboardMain9001+新注解@EnableHystrixDashboard )
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardMain9001 {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardMain9001.class,args);
}
}
所有Provider微服务提供类(8001/8002/8003)都需要监控依赖配置
<!-- actuator监控信息-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
5.启动9001 浏览器输入:http://localhost:9001/hystrix
断路器演示 (服务监控hystrixDashboard)
注意:监控时所有微服务提供者都需要在pom中引入监控依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
1.修改cloud-provider-hystrix-payment8001
在8001的主启动类中添加:
/**
* 此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑
* ServletRegistrationBean因为SpringBoot的默认路径不是 “/hystrix.stream"
* 只要在自己的项目里配置上下的servlet就可以了
*/
@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;
}
监控测试
启动9001,7001,8001
1.9001监控8001 监控地址:http://localhost:8001/hystrix.stream
2.浏览器输入 http://localhost:8001/payment/circuit/31 和 http://localhost:8001/payment/circuit/-31
上述测试通过 ok 刷新正确的正数多次后 点击监控
多次输入 http://localhost:8001/payment/circuit/-31 错误访问
稍微等一会,然后输入正确的访问 熔断就会关闭
使用流程:先访问正确地址,再访问错误地址,再正确地址,会发现图示断路器都是慢慢放开的
7色:每一种故障对应一种颜色
1圈
1线