38、微服务Hystrix 断路器 服务降级、熔断、限流
Hystrix理念:在分布式系统中多个服务互相调用,难免出现服务不可用或者程序出现异常导致客户端长时间不必要的占用,导致故障蔓延、乃至雪崩,Hystrix正是解决这一问题才名声大造
1、服务降级:服务器正忙,请稍后再试、不让客户端等待立即给客户一个友好的提示、fallback
2、服务熔断:服务达到最大的访问量之后,直接拒绝访问,然后调用服务降级,给出友好提示
3、服务限流:秒杀高并发操作,严禁一窝蜂请求拥挤、大家排队、一秒处理n个,有序的进行
创建cloud-provider-hystrix-payment8001 服务提供者项目 测试hystrix
编写pom.xml
<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-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-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
编写application.yml配置文件
server: port: 8001 spring: application: name: cloud-hystrix-payment-service eureka: client: register-with-eureka: true fetch-registry: true service-url: # defaultZone: http://localhost:7001/eureka defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
编写主启动类
@SpringBootApplication @EnableEurekaClient public class PaymentHystrixApplication8001 { public static void main(String[] args) { SpringApplication.run(PaymentHystrixApplication8001.class,args); } }
编写servicer逻辑层 PaymentService类
@Service @Slf4j public class PaymentService { public String paymentHystrix_ok(Integer id) { log.info(Thread.currentThread().getName() + "paymentHystrix_ok, id: "+ id + "哈哈"); return Thread.currentThread().getName() + "paymentHystrix_ok, id: "+ id + "哈哈"; } public String paymentHystrix_timeout(Integer id) { int timeOut= 3000; try { Thread.sleep(timeOut); } catch (InterruptedException e) { e.printStackTrace(); } log.info(Thread.currentThread().getName() + "paymentHystrix_timeout, id: "+ id + "哈哈" + "程序耗时:"+ timeOut); return Thread.currentThread().getName() + "paymentHystrix_timeout, id: "+ id + "哈哈" + "程序耗时:"+ timeOut; } }
编写业务controller类
@RestController @Slf4j public class PaymentController { @Resource private PaymentService paymentService; @GetMapping(value = "/payment/hystrix/ok/{id}") public String paymentHystrix_ok(@PathVariable("id") Integer id) { return paymentService.paymentHystrix_ok(id); } @GetMapping(value = "/payment/hystrix/timeout/{id}") public String paymentHystrix_timeout(@PathVariable("id") Integer id) { return paymentService.paymentHystrix_timeout(id); } }
测试访问正常的接口:http://localhost:8001/payment/hystrix/ok/66
测试方法发送延迟的接口:http://localhost:8001/payment/hystrix/timeout/66
4、使用JMeter 进行高并发测试 测试服务器在高并发情况下服务器被卡死
下载Jmeter: http://jmeter.apache.org/download_jmeter.cgi 下载zip压缩包
运行Jmeter,找到解压后的安装目录找到:jmeter.bat 双击打开
设置jmeter 的语言环境:D:\Program Files\apache-jmeter-5.4.1\bin\jmeter.properties 文件修改 language=zh_CN
添加线程组

添加Http请求

测试访问正常的接口:http://localhost:8001/payment/hystrix/ok/66 这个正常的请求变慢,被托卡 所以我们要做服务降级
5、创建服务消费者cloud-consumer-feign-hystrix-order80 这里采用Feign作为远程调用
编写pom.xml添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<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.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
编写application.yml配置文件
server: port: 80 spring: application: name: cloud-feign-hystrix-order-service eureka: client: register-with-eureka: true fetch-registry: true service-url: # defaultZone: http://localhost:7001/eureka defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
编写主启动类
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class OrderFeignHystrixMain80 { public static void main(String[] args) { SpringApplication.run(OrderFeignHystrixMain80.class,args); } }
编写Feign的远程调用接口
@Component @FeignClient(value = "cloud-hystrix-payment-service") public interface PaymentFeignHystrixService { @GetMapping(value = "/payment/hystrix/ok/{id}") public String paymentHystrix_ok(@PathVariable("id") Integer id); @GetMapping(value = "/payment/hystrix/timeout/{id}") public String paymentHystrix_timeout(@PathVariable("id") Integer id); }
编写controller业务类
@RestController @Slf4j public class OrderFeignHystrixController { @Resource private PaymentFeignHystrixService paymentFeignHystrixService; @GetMapping(value = "consumer/payment/hystrix/ok/{id}") public String paymentHystrix_ok(@PathVariable("id") Integer id){ String result = paymentFeignHystrixService.paymentHystrix_ok(id); return result; } @GetMapping(value = "consumer/payment/hystrix/timeout/{id}") public String paymentHystrix_timeout(@PathVariable("id") Integer id){ String result = paymentFeignHystrixService.paymentHystrix_timeout(id); return result; } }
测试浏览器访问:http://localhost/consumer/payment/hystrix/ok/666 发现可以立马得到响应
测试高并发测试,JMeter发起20000个请求同时去调用 服务提供者 然后用消费者服务调用 提供者服务 http://localhost/consumer/payment/hystrix/ok/666 发现需要漫长的等待,或者报服务异常,这样就有危险
所以我们要避免这个必须使用Hystrix的断路器
6、解决方案无非就三个
对方服务器(8001)超时了,调用者(80)不能一直等待,必须服务降级
对方服务器(8001)down机了,调用者(80)不能一直等待,必须服务降级
对方服务器(8001)OK了,调用者(80)自己出现故障,必须服务降级
7、服务提供者首先自己服务降级
修改PaymentService类
@HystrixCommand(fallbackMethod = "paymentHystrix_timeoutHandler",commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "2000") }) public String paymentHystrix_timeout(Integer id) { int timeOut= 3000; try { Thread.sleep(timeOut); } catch (InterruptedException e) { e.printStackTrace(); } log.info(Thread.currentThread().getName() + "paymentHystrix_timeout, id: "+ id + "哈哈" + "程序耗时:"+ timeOut); return Thread.currentThread().getName() + "paymentHystrix_timeout, id: "+ id + "哈哈" + "程序耗时:"+ timeOut; } public String paymentHystrix_timeoutHandler(Integer id){ return Thread.currentThread().getName()+ "服务器繁忙请稍候再试!"; }
修改主启动类加上注解:@EnableCircuitBreaker
@SpringBootApplication @EnableEurekaClient @EnableCircuitBreaker public class PaymentHystrixApplication8001 { public static void main(String[] args) { SpringApplication.run(PaymentHystrixApplication8001.class,args); } }
服务降级说明截图

8、服务消费者cloud-consumer-feign-hystrix-order80 自己服务降级
修改主启动类 添加注解:@EnableHystrix
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients @EnableHystrix public class OrderFeignHystrixMain80 { public static void main(String[] args) { SpringApplication.run(OrderFeignHystrixMain80.class,args); } }
修改controller业务类
@HystrixCommand(fallbackMethod = "paymentHystrix_timeoutHandler",commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1500") }) @GetMapping(value = "consumer/payment/hystrix/timeout/{id}") public String paymentHystrix_timeout(@PathVariable("id") Integer id){ String result = paymentFeignHystrixService.paymentHystrix_timeout(id); return result; } public String paymentHystrix_timeoutHandler(Integer id){ return "我是80客户端:"+Thread.currentThread().getName()+ "服务器繁忙请稍候再试!"; }
9、全局Fallback,设置
修改controller业务类

10、上述的服务降级方案,会导致业务逻辑处理和 fallback服务降级处理代码混乱,我们又要做的是将业务代码和降级处理隔离

修改application.yml配置文件

创建一个FallBack类 实现OpenFeign的远程调用接口,专门做熔断处理
@Component public class PaymentFeignHystrixFallBack implements PaymentFeignHystrixService { @Override public String paymentHystrix_ok(Integer id) { return "调用paymentHystrix_ok 时,对方服务器当时繁忙,请稍等重试!"; } @Override public String paymentHystrix_timeout(Integer id) { return "调用paymentHystrix_timeout 时,对方服务器当时繁忙,请稍等重试!"; } }
修改OpenFeign的远程调用接口

如果对方服务器出现请求超时、服务down机、都会出发服务降级处理
11、Hystrix的服务熔断测试(8001服务提供者测试)
编写PaymentService
@HystrixCommand(fallbackMethod = "paymentCircuitBreakerFallback",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(Integer id) { if(id<0){ throw new RuntimeException("id不能复数"); } String uuid = IdUtil.simpleUUID(); return Thread.currentThread().getName() + "调用成功, 流水号: "+ uuid ; } public String paymentCircuitBreakerFallback(Integer id) { return Thread.currentThread().getName() + "id为负数调用失败,系统繁忙请稍后再试 "; }
编写controller
@GetMapping(value = "/payment/hystrix/circuitBreaker/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id) {
return paymentService.paymentCircuitBreaker(id);
}
服务熔断再解释:三个状态 熔断关闭、熔断打开、熔断半开,先服务降级-》服务熔断打开-》服务恢复链路
熔断打开的条件:当客户端发的请求10个,失败率超过了百分之60 熔断打开,在时间窗口期检测到请求出现了成功的,熔断器关闭,服务链路自我修复
12、创建cloud-consumer-hystrix-dashboard9001 Hystrix的监控仪表盘
编写pom.xml依赖
<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-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-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
编写application.yml配置文件
server:
port: 9001
编写主启动类
@SpringBootApplication @EnableHystrixDashboard public class HystrixDashboardMain9001 { public static void main(String[] args) { SpringApplication.run(HystrixDashboardMain9001.class,args); } }
访问地址:http://localhost:9001/hystrix/ 到监控页面
13、服务提供者cloud-provider-hystrix-payment8001 要让 Hystrix 仪表盘监控

修改服务提供者的主启动类 注入这个Bean
@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; }

测试访问服务提供者的接口地址 多次访问
请求正确的:http://localhost:8001/payment/hystrix/circuitBreaker/1
请求失败的:http://localhost:8001/payment/hystrix/circuitBreaker/-1
查看仪表盘的监控信息


浙公网安备 33010602011771号