Hystrix是什么
在微服务的架构系统中,每个服务都可能会调用很多其他服务,被调用的那些服务就是依赖服务。有的时候某些依赖服务出现故障也是很正常的。Hystrix可以让我们在对服务间的调用进行控制,加入一些调用延迟或者依赖故障的容错机制。Hystrix通过将依赖服务进行资源隔离,进而组织某个依赖服务出现故障的时候,这种故障在整个系统所有的依赖服务调用中进行蔓延,同时Hystrix还提供故障时的fallback降级机制。总而言之,Hystrix通过这些方法帮助我们提升系统的可用性和稳定性。
服务降级
(例 :服务器忙, 请稍后再试) 遇到突发情况,异常,超时等,不让客户端等待并立刻返回一个友好提示,fallback
- 哪些情况会发出降级措施
- 异常
- 超时
- 服务熔断触发服务降级
- 服务降级代码实现
服务提供者
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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>spring-cloud-demo</artifactId>
<groupId>com.xx.job</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>payment-hystrix8083</artifactId>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.xx.job</groupId>
<artifactId>cloud-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</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.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
yml配置
server:
port: 8083
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka
spring:
application:
name: payment-hystrix
主启动类
package com.xx.job.paymenthystrix8083;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@EnableEurekaClient
@SpringBootApplication
@EnableCircuitBreaker //Hystrix开启注解 断路器
public class PaymentHystrix8083Application {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrix8083Application.class, args);
}
}
具体业务
package com.xx.job.paymenthystrix8083.controller;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.conf.HystrixPropertiesManager;
import com.xx.job.common.CommonResult;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
// 全局降级注解
@DefaultProperties(defaultFallback = "hystrixTimeoutHandleAll",commandProperties = {
@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_THREAD_TIMEOUT_IN_MILLISECONDS, value = "7000")
})
public class PaymentController {
@RequestMapping("/payment/hystrix/ok/{id}")
public CommonResult hystrixOk(@PathVariable String id){
return new CommonResult(200, "",Thread.currentThread().getName()+"-------------"+id);
}
// 方法级别降级注解
/*@HystrixCommand(fallbackMethod = "hystrixTimeoutHandle",commandProperties = {
@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_THREAD_TIMEOUT_IN_MILLISECONDS, value = "3000")
})*/
@HystrixCommand
@RequestMapping("/payment/hystrix/timeout/{id}")
public CommonResult hystrixTimeout(@PathVariable String id) throws InterruptedException {
System.out.println("ddddddddddddddddddd"+Thread.currentThread().getName());
Thread.sleep(6000);
// int a = 10/0;
return new CommonResult(200, "",Thread.currentThread().getName()+"-------------"+id);
}
/**
* 降级回调方法
* @return
*/
public CommonResult hystrixTimeoutHandle(@PathVariable String id) throws InterruptedException {
return new CommonResult(400, "",Thread.currentThread().getName()+"-------------"+id+"错误");
}
/**
* 全局降级回调方法
* @return
*/
public CommonResult hystrixTimeoutHandleAll() {
return new CommonResult(400, "",Thread.currentThread().getName()+"-------------"+"全局错误");
}
}
测试超时或者runtime异常是否会调用降级方法。
服务调用方
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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>spring-cloud-demo</artifactId>
<groupId>com.xx.job</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>order-hystrix-80</artifactId>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>com.xx.job</groupId>
<artifactId>cloud-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</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>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<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>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
yml文件
server:
port: 80
eureka:
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://localhost:7001/eureka
spring:
application:
name: order-hystrix-80
feign:
hystrix:
enabled: true
client:
config:
default:
#建立连接所用的时间,适用于网络状况正常的情况下,两端连接所需要的时间
ConnectTimeOut: 5000
#指建立连接后从服务端读取到可用资源所用的时间
ReadTimeOut: 10000
启动类
package com.xx.job.orderhystrix80;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableFeignClients
@EnableEurekaClient
@EnableCircuitBreaker
@SpringBootApplication
public class OrderHystrix80Application {
public static void main(String[] args) {
SpringApplication.run(OrderHystrix80Application.class, args);
}
}
feign运程调用接口
package com.xx.job.orderhystrix80.service;
import com.xx.job.common.CommonResult;
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;
@Component
@FeignClient(value = "PAYMENT-HYSTRIX",fallback = PaymentBackService.class)
public interface PaymentService {
@RequestMapping("/payment/hystrix/ok/{id}")
public CommonResult histrixOk(@PathVariable("id") String id);
@RequestMapping("/payment/hystrix/timeout/{id}")
public CommonResult histrixTimeOut(@PathVariable("id") String id);
}
PaymentBackService错误回调
package com.xx.job.orderhystrix80.service;
import com.xx.job.common.CommonResult;
import org.springframework.stereotype.Component;
@Component
public class PaymentBackService implements PaymentService{
public CommonResult histrixOk(String id) {
return new CommonResult(400, "PaymentBackService ---- histrixOk");
}
public CommonResult histrixTimeOut(String id) {
return new CommonResult(400, "PaymentBackService ---- histrixTimeOut");
}
}
控制层
package com.xx.job.orderhystrix80.controller;
import com.xx.job.common.CommonResult;
import com.xx.job.orderhystrix80.service.PaymentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class OrderController {
@Autowired
private PaymentService paymentService;
@RequestMapping("/consumer/hystrix/ok/{id}")
public CommonResult histrixOk(@PathVariable String id){
return paymentService.histrixOk(id);
}
@RequestMapping("/consumer/hystrix/timeout/{id}")
public CommonResult histrixTimeOut(@PathVariable String id){
return paymentService.histrixTimeOut(id);
}
}
服务熔断
涉及到的三个重要参数:快照时间窗、请求总阀值、错误百分比阀值
- 快照时间窗:断路器确定是否打开需要统计一些请求和错误数据,而统计的时间范围就是快照时间窗,默认为最近的10秒
- 请求总阀值:在快照时间窗内,必须满足请求总数阀值才有资格熔断,默认为20,意味着在10秒内,如果hystrix命令的调用次数不足20次,即使所有的请求都超时或者其他原因失败,断路器都不会打开。
- 错误百分比阀值:当请求总数在快照时间窗内超过了阀值,比如发生了30次调用,有15次发生了超时异常,也就是超过50%的错误百分比,在默认设定50%阀值情况下,这时候就会将断路器打开
代码
package com.xx.job.paymenthystrix8083.service;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.conf.HystrixPropertiesManager;
import org.springframework.stereotype.Service;
import java.util.UUID;
@Service
public class PaymentService {
// 服务熔断
@HystrixCommand(fallbackMethod = "hystrixTimeoutHandle",commandProperties = {
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ENABLED, value = "true"), // 是否开启断路器
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD, value = "10"), // 请求次数
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS, value = "10000"), // 时间窗口期
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE, value = "60") // 失败率达到多少后跳闸
})
public String paymentCircuitBreaker(Integer id){
if(id < 0){
throw new RuntimeException("不能是负数");
}
return Thread.currentThread().getName()+"-------------------------成功"+ UUID.randomUUID();
}
public String hystrixTimeoutHandle(Integer id){
return Thread.currentThread().getName()+"-------------------------失败";
}
}
服务限流
.....
hystrix-dashboard可视化流量监控
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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>spring-cloud-demo</artifactId>
<groupId>com.xx.job</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.xx.job</groupId>
<artifactId>hystrix-dashboard9001</artifactId>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>com.xx.job</groupId>
<artifactId>cloud-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</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>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<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>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
yml文件
server:
port: 9001
hystrix:
dashboard:
proxy-stream-allow-list: "localhost"
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka
spring:
application:
name: hystrix-dashboard9001
主启动类
package com.xx.job.hystrixdashboard9001;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
@EnableHystrixDashboard
@EnableEurekaClient
@SpringBootApplication
public class HystrixDashboard9001Application {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboard9001Application.class, args);
}
}
访问 http://127.0.0.1:9001/hystrix
注意被监控的实例必须要有这两个jar
<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>
被监控实例主启动类
package com.xx.job.paymenthystrix8083;
import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
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;
@EnableEurekaClient
@SpringBootApplication
@EnableCircuitBreaker //Hystrix开启注解 断路器
public class PaymentHystrix8083Application {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrix8083Application.class, args);
}
@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;
}
}
成功监控页面
具体demo spring-cloud-demo: spring-cloud-demo试例 工程payment-hystrix8083提供者
order-hystrix-80消费者