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
效果图