SpringCloud第七节内容精简(上),Hystrix断路器和服务降级

    技术2022-07-11  96

     Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,如超时,异常灯,Hystrix能够保证 在一个以来出问题的情况下,不会导致整体服务失败,避免级联故障 ,以提高分布式系统的弹性。

    通过断路器的故障监控,向调用方返回一个符合预期的,可处理的备选响应,而不是 长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的现成不会被长时间,不必要的占用,避免故障在分布式系统中的蔓延,乃至雪崩。

     

    分为服务降级,服务熔断,服务限流

    以下是服务降级

    创建项目

    8001的hystrix项目,注意这里是feign配合hystrix学习 

    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>cloud2020</artifactId> <groupId>com.atguigu.springcloud</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> <!--eureka client--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!--web--> <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><!-- 引入自己定义的api通用包,可以使用Payment支付Entity --> <groupId>com.atguigu.springcloud</groupId> <artifactId>cloud-api-commons</artifactId> <version>${project.version}</version> </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> </project>

    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){ try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } return "线程池:"+Thread.currentThread().getName()+"paymentInfo_TimeOut,id:"+id+"耗时3秒"; } }

    控制器

    @RestController @Slf4j public class PaymentController { @Resource private PaymentService paymentService; @Value("${server.port") private String serverPost; @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; } }

     启动类

    @SpringBootApplication @EnableEurekaClient public class PaymentHystrixMain8001 { public static void main(String[] args) { SpringApplication.run(PaymentHystrixMain8001.class,args); } }

    然后借助JMeter压力测试

    https://blog.csdn.net/bailaoshi666/article/details/107067478

     

    80项目模块

    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>cloud2020</artifactId> <groupId>com.atguigu.springcloud</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> <!--eureka client--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!-- 引入自己定义的api通用包,可以使用Payment支付Entity --> <dependency> <groupId>com.atguigu.springcloud</groupId> <artifactId>cloud-api-commons</artifactId> <version>${project.version}</version> </dependency> <!--web--> <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> </project>

    service层,一定要feign注解加接口啊 

    @Component @FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT") 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); }

    application.yml

    server: port: 80 eureka: client: register-with-eureka: false service-url: defaultZone: http://eureka7001.com:7001/eureka/ feign: hystrix: enabled: true

    控制器

    @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); } }

     

    压力8001接口测试再走80接口会报超时错误

    写降级方法

    修改8001的service层,超时3秒走另一个

    @Service public class PaymentService { public String paymentInfo_Ok(Integer id){ return "线程池:"+Thread.currentThread().getName()+"paymentInfo_ok,id:"+id; } @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",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+"耗时5秒"; } public String paymentInfo_TimeOutHandler(Integer id){ return "线程池:"+Thread.currentThread().getName()+"paymentInfo_TimeOutHandler,id:"+id+"拦截方法"; } }

     

    启动类要加@EnableCircuitBreaker即可

    @SpringBootApplication @EnableEurekaClient @EnableCircuitBreaker public class PaymentHystrixMain8001 { public static void main(String[] args) { SpringApplication.run(PaymentHystrixMain8001.class,args); } }

    方法改成int age = 10/0计算错误也可以拦截

     

    另外关于Hystrix的注解,建议修改完重启微服务,热部署对于这个注解不是及时反应

     

    一般在项目中,断路器Hystrix一般是写在消费者,就是80端口的服务。所以下面来为80项目添加断路器

    其实主要就是application.yml加启动,启动类加注解,服务加注解

    application.yml

    server: port: 80 eureka: client: register-with-eureka: false service-url: defaultZone: http://eureka7001.com:7001/eureka/ feign: hystrix: enabled: true

    启动类

    @SpringBootApplication @EnableFeignClients @EnableHystrix public class OrderHystrixMain80 { public static void main(String[] args) { SpringApplication.run(OrderHystrixMain80.class,args); } }

    控制器

    @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}") @HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",commandProperties = { @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="1500") }) public String paymentInfo_TimeOut(@PathVariable("id") Integer id){ return paymentHystrixService.paymentInfo_TimeOut(id); } public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id){ return "我是消费者80,对方系统繁忙"; } }

    效果。写上int age  = 10/0。效果如下

    这里让我想到个问题 ,为什么 8001的断路器写在service层,而80的断路器在控制层。

    后来发现原来是,视频老师偷懒,8001的service层是class层,实际就是平常的serviceImpl层,可以写实现,也可以写控制器上。而80项目的service是interface,接口,只能写在serviceImpl,但是视频中为了简便,没有serviceImpl,直接控制器实现,所以这就是写在控制器的原因,其实写serviceImpl即可 。仔细查看项目,8001的service层(实际是serviceImpl层)加了 @Service注解。而80项目的 service层加了 @Component扫描。

     

    之后,我修改了8001,把断路器从service层(实际是serviceImpl层)移动到了控制层,效果依旧可以。从80访问依旧走80的callback

     

    接下来考虑到效率问题,如何1对多的断路器,就是默认断路器

    主要是 @DefaultProperties 和 @HystrixCommand

    代码只修改这个。要注意这个默认的断路器方法不能写参数

    @RestController @Slf4j @DefaultProperties(defaultFallback = "payment_Global_FallbackMethod") 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}") /*@HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",commandProperties = { @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="1000") })*/ @HystrixCommand public String paymentInfo_TimeOut(@PathVariable("id") Integer id){ return paymentHystrixService.paymentInfo_TimeOut(id); } public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id){ return "我是消费者80,对方系统繁忙"; } public String payment_Global_FallbackMethod(){ return "我是消费者80的全局断路器"; } }

    效果如下

     

    考虑完1对多的断路器,要考虑如何写代码解耦,更优化

    写个类,继承service层并扫描进spring

    @Component public class PaymentFallbackService implements PaymentHystrixService { @Override public String paymentInfo_Ok(Integer id) { return "PaymentFallbackService-okokkokokokok"; } @Override public String paymentInfo_TimeOut(Integer id) { return "PaymentFallbackService.timeoutimeout"; } }

     

    service层加fallback

    @Component @FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = PaymentFallbackService.class) 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); }

     

    注意application.yml要开启 

    server: port: 80 eureka: client: register-with-eureka: false service-url: defaultZone: http://eureka7001.com:7001/eureka/ feign: hystrix: enabled: true

     

    效果图

     

    Processed: 0.014, SQL: 9