SpringCloud&alibaba学习笔记系列7
SpringCloud&alibaba学习笔记系列
该笔记仅作为个人学习使用。
第七章Hystrix断路器
7.1概述
7.1.1分布式系统面临的问题
复杂分布式体系结构中的应用程序有数十个依赖关系,每一个依赖关系在某些时候将不可避免地失败。
如上图的请求需要调用A、P、H、I四个服务,如果一切顺利则没有什么问题,关键如果I服务超时会出现什么情况?
显然,如果I服务出现问题就有可能导致整这个调用链出现问题,严重可能整个系统瘫痪。也就是我们说的服务雪崩。
服务雪崩:
多个微服务之间调用的时候,假设服务A调用服务B和服务C,微服务B和微服务C又调用其他的微服务,这就是所谓的”扇出“。如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统奔溃,也就是服务雪崩。
对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序和系统。
所以,通常当你发现一个模块下的某个实例失败后,这个时候这个模块依然还会接收流量,然后这个有问题的模块还调用了其他的模块,这样就会发生级联故障,或者叫做雪崩。
7.1.2hystrix是什么
官网:https://github.com/Netflix/Hystrix/wiki
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布是系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
“断路器”本身使用开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似于熔断保险丝),向调用方返回一个符合预期的,可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中蔓延,乃至崩溃。
7.1.3hystrix能干嘛
服务降级
服务熔断
接近实时的监控
。。。
7.2Hystrix重要概念
7.2.1服务降级fallback
向调用方返回一个符合预期的,可处理的备选响应或者友好的提示,而不是让客户长时间等待或者抛出一个调用方无法处理的异常。
哪些情况下会发出服务降级:
- 程序运行异常
- 超时
- 服务熔断触发服务降级
- 线程池/信号量打满也会导致服务降级
7.2.2服务熔断break
服务熔断就好比是保险丝,当达到最大服务访问后,直接拒绝,拉闸限电,然后调用服务降级的方法并返回友好提示,其实就是服务降级的触发条件,当响应恢复正常后,可以恢复服务的正常调用。
7.2.3服务限流followlimit
限定在指定的时间内只能有指定数量的请求进来。在秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行。
7.3Hystrix案例
7.3.1构建服务提供者
1、新建cloud-provider-hystrix-payment8001
2、POM文件
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>springcloud2020</artifactId> <groupId>com.jzt</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-provider-hystrix-payment8001</artifactId> <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.jzt</groupId> <artifactId>cloud-api-common</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.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</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> <version>1.18.14</version> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies></project>
3、YML配置文件
server: port: 8001spring: application: name: cloud-payment-hystrix-serviceeureka: client: register-with-eureka: true #表示将自己注册到eurekaserver上,默认为true fetch-registry: true #是否从Eurekaserver抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true,才能配合ribbon使用负载均衡 service-url: defaultZone: http://localhost:7001/eureka #单机版# defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka #集群版
4、主启动类
package com.jzt;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;/** * 功能描述: * * @Author: sj * @Date: 2020/12/4 21:36 */@SpringBootApplicationpublic class PaymentHystrixMain8001 { public static void main(String[] args) { SpringApplication.run(PaymentHystrixMain8001.class, args); }}
5、业务类
controller
package com.jzt.controller;import com.jzt.service.PaymentService;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Value;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;/** * 功能描述: * * @Author: sj * @Date: 2020/12/4 21:46 */@RestController@Slf4jpublic class PaymentController { @Value("${server.port}") private String serverPort; @Resource private PaymentService paymentService; @RequestMapping("payment/hystrix/ok/{id}") public String paymentInfo_OK(@PathVariable("id") Integer id){ String result = paymentService.paymentInfo_OK(id); log.info("result ---:" result); return result; } @RequestMapping("payment/hystrix/timeout/{id}") public String paymentInfo_Timeout(@PathVariable("id") Integer id){ String result = paymentService.paymentInfo_Timeout(id); log.info("result ---:" result); return result; }}
service
package com.jzt.service;import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;/** * 功能描述: * * @Author: sj * @Date: 2020/12/4 21:37 */@Servicepublic class PaymentService { public String paymentInfo_OK(Integer id){ return "线程池: " Thread.currentThread().getName() "paymentInfo_OKm id:" id "\t" "O(∩_∩)O哈哈~"; } public String paymentInfo_Timeout(Integer id){ int time = 3; try{ TimeUnit.SECONDS.sleep(time); }catch (Exception e){ e.printStackTrace(); } return "线程池: " Thread.currentThread().getName() "paymentInfo_Timeout id:" id "\t" "O(∩_∩)O哈哈~, 耗时:" time; }}
6、测试
1)启动eureka7001;
2)启动cloud-provider-hystrix-payment8001;
3)访问:http://localhost:8001/payment/hystrix/ok/1
http://localhost:8001/payment/hystrix/timeout/1
7.3.2JMeter高并发测试
Apache JMeter是Apache组织开发的基于Java的压力测试工具。官网:https://jmeter.apache.org/,具体使用见《Jmeter入门教程》
首先,我们开启Jmeter,来40000个并发打到我们的8001微服务上。
线程组:
请求:
配置好后,启动。
再来访问http://localhost:8001/payment/hystrix/ok/1
我们发现源码几乎无延迟的访问,现在已经开始转圈圈有一定延迟后才有响应了
问题:
为什么会卡死:
Tomcat默认的线程数被打满,没有多余线程去处理和分担压力了。
上面还只是对服务提供者8001,加入此时外部的消费者80也来访问服务,那消费者服务只能干等着了,服务端8001直接被拖死。
7.3.3构建80服务消费者
1、新建服务消费者cloud-consumer-feign-hystrix-order80
2、POM文件
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>springcloud2020</artifactId> <groupId>com.jzt</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-consumer-feign-hystrix-order80</artifactId> <dependencies> <!--openfeign--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!--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>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.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</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> <version>1.18.14</version> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies></project>
3、YML文件
server: port: 80eureka: client: register-with-eureka: true #表示将自己注册到eurekaserver上,默认为true fetch-registry: true #是否从Eurekaserver抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true,才能配合ribbon使用负载均衡 service-url: defaultZone: http://localhost:7001/eureka #单机版# defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka #集群版
4、主启动类
package com.jzt;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.openfeign.EnableFeignClients;/** * 功能描述: * * @Author: sj * @Date: 2020/12/5 17:23 */@SpringBootApplication@EnableFeignClientspublic class OrderHystrixMain80 { public static void main(String[] args) { SpringApplication.run(OrderHystrixMain80.class, args); }}
5、业务类
service:
package com.jzt.service;import org.springframework.cloud.openfeign.FeignClient;import org.springframework.stereotype.Component;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;/** * 功能描述: * * @Author: sj * @Date: 2020/12/5 17:29 */@Component@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT")public interface PaymentHystrixService { @RequestMapping("payment/hystrix/ok/{id}") public String paymentInfo_OK(@PathVariable("id") Integer id); @RequestMapping("payment/hystrix/timeout/{id}") public String paymentInfo_Timeout(@PathVariable("id") Integer id);}
controller
package com.jzt.controller;import com.jzt.service.PaymentHystrixService;import lombok.extern.slf4j.Slf4j;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;/** * 功能描述: * * @Author: sj * @Date: 2020/12/5 17:32 */@RestController@Slf4jpublic class OrderHystrixController { @Resource private PaymentHystrixService paymentHystrixService; @RequestMapping("/consumer/payment/hystrix/ok/{id}") public String paymentInfo_OK(@PathVariable("id") Integer id){ String result = paymentHystrixService.paymentInfo_OK(id); return result; } @RequestMapping("/consumer/payment/hystrix/timeout/{id}") public String paymentInfo_Timeout(@PathVariable("id") Integer id){ String result = paymentHystrixService.paymentInfo_Timeout(id); return result; }}
6、正常测试
启动Eureka7001;
启动生产者微服务cloud-provider-hystrix-payment8001;
启动消费者微服务cloud-consumer-feign-hystrix-order80;
访问:http://localhost/consumer/payment/hystrix/ok/1
可以正常访问,响应速度也很快。
7、高并发测试
继续使用Jmeter高并发测试。
同时再次访问:
http://localhost/consumer/payment/hystrix/ok/1
发现服务响应时间已经比较长了。
正因为有上述这些故障或者不佳的表现,才有我们的降级、容错、限流等技术的诞生。
8、如何解决
1)对于服务提供者(8001)超时了,服务调用者(80)不能一直卡死等待,必须有服务降级;
2)对于服务提供者(8001)宕机了,服务调用者(80)不能一直卡死等待,必须有服务降级;
3)对于服务提供者(8001)ok,服务调用者(80)自己出故障了或者有自我要求(服务等待时间小于服务提供者执行时间),自己处理降级。
7.4服务降级
7.4.1服务降级配置
通过@HystrixCommand注解能对某一个接口定制 Hystrix的超时时间,并返回兜底的fallback方法。
7.4.2从服务提供者8001降级
设置自身调用超时时间的峰值,峰值内可以正常运行;超过了需要有兜底的方法处理,做服务降级fallback;
修改8001业务类:
@HystrixCommand(fallbackMethod = "paymentInfo_TimeoutHandler", commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMillisends", value = "3000") }) public String paymentInfo_Timeout(Integer id){ int time = 5; try{ TimeUnit.SECONDS.sleep(time); }catch (Exception e){ e.printStackTrace(); } return "线程池: " Thread.currentThread().getName() "paymentInfo_Timeout id:" id "\t" "O(∩_∩)O哈哈~, 耗时:" time; } public String paymentInfo_TimeoutHandler(Integer id){ return "线程池: " Thread.currentThread().getName() "paymentInfo_TimeoutHandler id:" id "\t" "o(╥﹏╥)o"; }
使用了@HystrixCommand注解,需要在主启动类中添加注解**@EnableCircuitBreaker**。
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})@EnableEurekaClient@EnableCircuitBreaker//添加该注解,使得@HystrixCommand注解生效。public class PaymentHystrixMain8001 { public static void main(String[] args) { SpringApplication.run(PaymentHystrixMain8001.class, args); }}
测试,再次访问:http://localhost:8001/payment/hystrix/timeout/1
我们再人为制造一个异常,看Hystrix能否为我们兜底。
@HystrixCommand(fallbackMethod = "paymentInfo_TimeoutHandler", commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000") }) public String paymentInfo_Timeout(Integer id){ int time = 5; int a = 1/0;//异常报错 try{ TimeUnit.SECONDS.sleep(time); }catch (Exception e){ e.printStackTrace(); } return "线程池: " Thread.currentThread().getName() "paymentInfo_Timeout id:" id "\t" "O(∩_∩)O哈哈~, 耗时:" time; } public String paymentInfo_TimeoutHandler(Integer id){ return "线程池: " Thread.currentThread().getName() "paymentInfo_TimeoutHandler,系统忙,请稍后再试! o(╥﹏╥)o"; }
再次访问,结果依然如下:
通过以上操作,一旦调用服务方法失败并抛出了错误信息之后,会自动调用@HystrixCommand标注好的fallbackMethod调用类中的指定的方法。从而返回一个用户可以接受的响应,达到服务降级的效果。
7.4.3从服务消费者80降级
从订单服务侧进行降级。80订单微服务,也可以更好的保护自己,依照上面的配置进行客户端降级保护。
修改yml配置:
server: port: 80eureka: client: register-with-eureka: true #表示将自己注册到eurekaserver上,默认为true fetch-registry: true #是否从Eurekaserver抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true,才能配合ribbon使用负载均衡 service-url: defaultZone: http://localhost:7001/eureka #单机版# defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka #集群版feign: hystrix: enabled: true
feign.hystrix.enabled=true配置是springcloud开启Hystrix功能。
修改80微服务业务类:
@HystrixCommand(fallbackMethod = "paymentInfo_TimeoutHandler", commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500") }) @RequestMapping("/consumer/payment/hystrix/timeout/{id}") public String paymentInfo_Timeout(@PathVariable("id") Integer id){ String result = paymentHystrixService.paymentInfo_Timeout(id); return result; } public String paymentInfo_TimeoutHandler(Integer id){ return "我是消费者80: " Thread.currentThread().getName() "paymentInfo_TimeoutHandler,系统忙,请稍后再试! o(╥﹏╥)o"; }
同时需要在主启动类上加上**@EnableHystrix**注解:
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})@EnableFeignClients@EnableHystrixpublic class OrderHystrixMain80 { public static void main(String[] args) { SpringApplication.run(OrderHystrixMain80.class, args); }}
运行测试,访问:http://localhost/consumer/payment/hystrix/timeout/1
说明订单微服务在1.5s后调用了paymentInfo_TimeoutHandler方法,进行了降级。
7.4.4全局服务降级DefaultProperties
通过上面分别在服务提供者8001和服务消费者80侧进行降级,虽然达到了服务降级的作用,但是如果每一个业务方法对应一个FallBack方法,那么就会造成代码膨胀和冗余。
我们可以定义一个全局的服务降级的方式来将通用的服务降级抽取,然后与自定义的分开。
实现方法:
1、在service中新增一个方法paymentInfo_Timeout2
@HystrixCommand public String paymentInfo_Timeout2(Integer id){ int a = 1/0;//异常报错 return "线程池: " Thread.currentThread().getName() "paymentInfo_Timeout2 id:" id "\t" "O(∩_∩)O哈哈~"; }
2、在service中新增fallback全局处理方法payment_global_fallBackMethod
//全局fallback方法 public String payment_global_fallBackMethod(){ return "payment_global_fallBackMethod,系统忙,请稍后再试! o(╥﹏╥)o"; }
3、需要在service类上添加注解@DefaultProperties
@Service@Slf4j@DefaultProperties(defaultFallback = "payment_global_fallBackMethod")public class PaymentService {...}
4、在controller中添加对应的接口
@RequestMapping("/payment/hystrix/timeout2/{id}") public String paymentInfo_Timeout2(@PathVariable("id") Integer id){ String result = paymentService.paymentInfo_Timeout2(id); log.info("result ---:" result); return result; }
5、测试,访问:http://localhost:8001/payment/hystrix/timeout2/1
说明我们配置的全局fallback方法起作用了。
7.4.5通配服务降级FeignFallBack
上一小结我们通过全聚德服务降级配置方法,解决了多个方法配置fallback的问题,但现在还存在另一个问题,就是fallback方法与业务逻辑耦合,我们需要一种方法能够解耦。
下面我们通过案例使用通配服务降级的方式来进行业务与降级解耦。
案例的服务降级处理时在客户端80实现完成的,与服务端8001没有关系只需要为Feign客户端定义的接口添加一个服务降级处理的实现类即可实现解耦。
根据cloud-onsumer-feign-hystrix-order80已经有的PaymentHystrixService接口,重新新建类(PaymentFallbackService)实现该接口,统一为接口里面的方法进行异常处理。
具体步骤:
1、在服务消费者80侧的service层添加PaymentHystrixServiceImpl类,并继承PaymentHystrixService
package com.jzt.service;import org.springframework.stereotype.Component;/** * 功能描述: * * @Author: sj * @Date: 2020/12/6 10:49 */@Componentpublic class PaymentHystrixServiceImpl implements PaymentHystrixService { @Override public String paymentInfo_OK(Integer id) { return "PaymentHystrixServiceImpl paymentInfo_OK, O(∩_∩)O哈哈~"; } @Override public String paymentInfo_Timeout(Integer id) { return "PaymentHystrixServiceImpl paymentInfo_Timeout, o(╥﹏╥)o"; }}
2、在PaymentHystrixService的注解@FeignClient中添加fallback
package com.jzt.service;import org.springframework.cloud.openfeign.FeignClient;import org.springframework.stereotype.Component;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;/** * 功能描述: * * @Author: sj * @Date: 2020/12/5 17:29 */@Component@FeignClient(value = "CLOUD-PAYMENT-HYSTRIX-SERVICE", fallback = PaymentHystrixServiceImpl.class)public interface PaymentHystrixService { @RequestMapping("payment/hystrix/ok/{id}") public String paymentInfo_OK(@PathVariable("id") Integer id); @RequestMapping("payment/hystrix/timeout/{id}") public String paymentInfo_Timeout(@PathVariable("id") Integer id);}
3、测试
启动80;访问:http://localhost/consumer/payment/hystrix/timeout/1
我们发现这是调用的8001服务侧的fallback,因为该服务我们自己认为制造了1/0的异常。
如果这是我们把8001微服务手动停止,再次访问http://localhost/consumer/payment/hystrix/timeout/1
我们发现,在80侧已经PaymentHystrixServiceImpl的fallback方法,帮我们进行了降级处理。
7.5服务熔断
7.5.1服务熔断简介
熔断机制是应对服务雪崩效应的一种微服务链路保护机制。当扇出链路的某个微服务出错不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的相应信息。
当检测到该节点微服务调用响应正常后,恢复调用链路。
在SpringCloud框架里,熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省时5秒内20次调用失败,就会启动熔断机制。熔断机制的注解是**@HystrixCommand**。
7.5.2实现服务熔断
1、修改cloud-provider-hystrix-payment8001微服务
在service中新增方法:
//服务熔断 @HystrixCommand(fallbackMethod = "paymentCircuitBreak_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 paymentCircuitBreak(@PathVariable("id") Integer id){ if(id < 0){ throw new RuntimeException("----id 不能为负数"); } String serialNumber = IdUtil.simpleUUID(); return Thread.currentThread().getName() " 调用成功,id:" serialNumber; } public String paymentCircuitBreak_fallBack(@PathVariable("id") Integer id){ return "id 不能为负数,请稍后再试! o(╥﹏╥)o, id:" id; }
备注,因为使用了IdUtil.simpleUUID(),所以需要导入我们之前封装的服务,
<dependency> <groupId>com.jzt</groupId> <artifactId>cloud-api-common</artifactId> <version>${project.version}</version></dependency>
它里面依赖了hutool-all工具类:
<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.1.0</version> </dependency>
2、controller
//服务熔断 @RequestMapping("/payment/hystrix/circuitBreak/{id}") public String paymentCircuitBreak(@PathVariable("id") Integer id){ String result = paymentService.paymentCircuitBreak(id); log.info("result ---:" result); return result; }
3、测试
正常访问数据:http://localhost:8001/payment/hystrix/circuitBreak/1
在10秒内反复快速的访问,也就是在十秒内,异常访问次数超过60%:http://localhost:8001/payment/hystrix/circuitBreak/-1
当我们再次访问正常数据时:http://localhost:8001/payment/hystrix/circuitBreak/1
发现也返回了异常fallback响应,这就是熔断了;
过一段时间,我们再次访问正常数据:
发现链路又正常了,这就是上面说的服务熔断后得链路恢复机制。
7.5.3服务熔断总结
1、服务熔断机制图
2、熔断类型
- 熔断打开:请求不在进行调用当前服务,内部设置时钟一般为MTTR(平均故障处理时间),当打开时长达到所设时钟则进入半熔断状态;
- 熔断关闭:熔断关闭不会对服务进行熔断;
- 熔断半开:部分请求根据规则调用当前服务,如果请求成功且符合规则则认为当前服务恢复正常,关闭熔断。
3、断路器起作用的条件
//服务熔断 @HystrixCommand(fallbackMethod = "paymentCircuitBreak_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秒;
- 请求总数阈值:在快照时间窗内,必须满足请求总数阈值才有资格熔断。默认为20,意味着在10秒内,如果该hystrix命令的调用次数不足20次,及时所有的请求都超时或其他原因失败,断路器都不会打开。
- 错误百分比阈值:当请求总数在快照时间窗内超过了阈值,比如发生了20次调用,如果在这20次调用中,由15次发生了超时异常,也就是超过50%的错误百分比,在默认设定50%阈值情况下,这时候就会将断路器打开。
只有满足了以上条件,断路器才能够起作用。
4、断路器打开之后
1)再有请求调用的时候,将不会调用主逻辑,而是直接调用降级fallback。通过断路器,实现了自动地发现错误并将降级逻辑切换为主逻辑,减少响应延迟的效果。
2)hystrix为我们实现了自动回复功能。当断路器打开,对主逻辑进行熔断之后,hystrix会启动一个休眠时间窗,在这个时间窗内,降级逻辑是临时的成为主逻辑,当休眠时间窗到期,断路器将进入搬开状态,释放一次请求到原来的主逻辑上,如果此次请求正常返回,那么断路器继续闭合,主逻辑恢复,如果这次请求依然有问题,断路器继续进入打开状态,休眠时间窗重新计时。
7.6服务限流
省略。到alibaba的Sentinel时在讲解。
7.7服务监控HystrixDashboard
7.7.1概述
除了隔离依赖服务的调用以外,Hystrix还提供了准实时的调用监控(Hystrix Dashboard),Hystrix会持续地记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行多少请求多少成功,多少失败等。Netflix通过hystrix-metrics-event-stream项目实现了对以上指标的监控。SpringCloud也提供了Hystrix Dashboard的整合,对监控内容转化成可视化界面。
7.7.1仪表盘9001构建
1、新建cloud-consumer-hystrix-dashboard9001
2、POM文件
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>springcloud2020</artifactId> <groupId>com.jzt</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-consumer-hystrix-dashboard9001</artifactId> <dependencies> <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> <version>1.18.14</version> <optional>true</optional> </dependency> </dependencies></project>
3、yml配置文件:
server: port: 9001
4、主启动类
package com.jzt;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;/** * 功能描述: * * @Author: sj * @Date: 2020/12/6 16:14 */@SpringBootApplication@EnableHystrixDashboard//启动仪表盘注解public class HystrixDashboardMain9001 { public static void main(String[] args) { SpringApplication.run(HystrixDashboardMain9001.class, args ); }}
4、注意点
所有Provider微服务提供类(8001、8002、8003)需要监控依赖的配置。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
5、启动运行9001
启动后访问:http://localhost:9001/hystrix
7.7.2HystrixDashboard演示
1、修改cloud-provider-hystrix-payment8001
1)在POM文件中需要添加依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
这个之前我们已经添加过了。
2)在主启动类中添加@EnableCircuitBreaker注解和如下内容:
package com.jzt;import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;import org.springframework.boot.web.servlet.ServletRegistrationBean;import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;import org.springframework.cloud.netflix.eureka.EnableEurekaClient;import org.springframework.context.annotation.Bean;import javax.servlet.ServletRegistration;/** * 功能描述: * * @Author: sj * @Date: 2020/12/4 21:36 */@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})@EnableEurekaClient@EnableCircuitBreakerpublic class PaymentHystrixMain8001 { public static void main(String[] args) { SpringApplication.run(PaymentHystrixMain8001.class, args); } /** * 此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑; * ServletRegistrationBean因为springboot的默认路径不是"/hystrix.stream"; * 只要在自己的项目里配置上下面的servlet就可以了。 * @return */ @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; }}
注意,新版本Hystrix需要在主启动类中指定监控路径。
2、仪表盘9001监控8001
配置监控8001:
点击monitor Stream。
然后不断地访问:http://localhost:8001/payment/hystrix/circuitBreak/1
查看仪表盘变化:
在不断访问:http://localhost:8001/payment/hystrix/circuitBreak/-1
3、如何看仪表板监控结果
1)7色
2)1圈
实心圆:共有两种含义。它通过颜色的变化代表了实例的健康程度,它的监控程度从绿色<黄色<橙色<红色递减。
该实心圆除了颜色的变化外,它的大小也会根据实例的请求流量发生变化,流量越大该实心圆就越大。所以通过该实心圆的展示,就可以在大量的事例中快速的发现故障实例和高压力实例。
3)1线
曲线:用来记录2分钟内流量的相对变化,可以通过它来观察到流量的上升和下降趋势。
4)整图说明: