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