简介:
Hystrix 是一个用于处理分布式系统的延迟和容错的开源库, 在分布式系统中,许多不可避免的调用会失败, 比如超时,异常等。Hystrix 能够保证在一个依赖出现问题的情况下,不会导致整体服务失败,避免级联故障,提高分布式系统的弹性。
“断路器” 本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似保险熔断),向调用方法返回一个符合预期的,可处理的被选响应(FallBack), 而不是长时间的等待或者抛出调用方法无法处理的异常,这样就保证了服务调用方的线程不会长时间,不必要地占用,从而避免了故障在分布式系统中的蔓延,从而导致雪崩效应。
前言:使用的consul注册中心和openfeign(消费端使用)
环境准备
POM
- Provider
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.live</groupId>
<artifactId>hystrix-provider8001</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>2.3.1.RELEASE</version>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
- Consumer
<!--多一个openfeign依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
YML
- Provider
server:
port: 8001
spring:
application:
name: hystrix-provider8001
cloud:
consul:
host: localhost
port: 8500
discovery:
service-name: ${spring.application.name}
instance-id: provider8001
ip-address: true
-
Consumer
-
ribbon.ReadTimeout: 从服务器获取服务的最大时间
-
ribbon.ConnectTimeout: 连接至服务器的最大时间
-
logging.level.xx.xx.Xxxx: 开启openfeign的日志
import feign.Logger; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class OpenfeignLogConfig { @Bean Logger.Level level() { return Logger.Level.FULL; } }
-
feign.hystrix.enabled: 启用feign对hystrix的支持
-
server:
port: 80
spring:
application:
name: hystri-consumer80
cloud:
consul:
host: localhost
port: 8500
discovery:
prefer-ip-address: true
service-name: ${spring.application.name}
instance-id: consumer80
ribbon:
ReadTimeout: 6000
ConnectTimeout: 6000
logging:
level:
com.live.service.PaymentService: debug
feign:
hystrix:
enabled: true
启动类
- Provider
- @EnableCircuitBreaker: 启用Hystrix
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
@SpringBootApplication
@EnableCircuitBreaker
public class HystrixProvider8001 {
public static void main(String[] args) {
SpringApplication.run(HystrixProvider8001.class,args);
}
}
- Consumer
- @EnableCiruitBreaker
- @EnableFeignClients: 启用Feign
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableCircuitBreaker
@EnableFeignClients
public class HystrixConsumer80 {
public static void main(String[] args) {
SpringApplication.run(HystrixConsumer80.class,args);
}
}
对每个方法定义FallBack Method
Service
在需要降级的方法上标识注解
@HystrixCommand
降级方法和service定义在同一个类中
@HystrixCommand 属性 描述 fallbackMethod 降级方法的方法名 commandProperties 设置Hystrix的各种属性
@HystrixProperty 属性 描述 execution.isolation.thread.timeoutInMilliseconds 如果这个方法执行时间超过了所设置的就调用fallback circuitBreaker.enabled boolean 开启断路器 circuitBreaker.requestVolumeThreshold 请求次数 circuitBreaker.sleepWindowInMilliseconds 窗口期 circuitBreaker.errorThresholdPercentage 请求失败百分比(和上三个同时使用,在一个窗口期内,有x次请求,且请求错误达到x的百分比就开启断路器) 更多的属性查看HystrixCommandProperties类
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.concurrent.TimeUnit;
@Service
@Slf4j
public class PaymentService {
@HystrixCommand(fallbackMethod = "timeout_fallback",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "4000")
})
@GetMapping(value = "/timeoutTest")
public String timeoutTest() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
log.info("====Timout Exception ヾ(≧ ▽ ≦)ゝ");
}
return Thread.currentThread().getName() + " => provider Timeout Test";
}
@HystrixCommand(fallbackMethod = "exception_fallback")
@GetMapping(value = "/exceptionTest")
public String exceptionTest() {
int i = 10 / 0;
return Thread.currentThread().getName() + " => provider Exception Test";
}
public String timeout_fallBack() {
return Thread.currentThread().getName() + " => This is the provider timeout_fallback";
}
public String exception_fallback() {
return Thread.currentThread().getName() + " => This is the provider exception_fallback";
}
}
局部FallBack
- 在类上标识注解 @DefaultProperties(defaultFallback=“方法名”)
- 定义一个Fallback方法
- 在需要Fallback的方法上标识注解 @HystrixCommand 即可
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.concurrent.TimeUnit;
@Service
@DefaultProperties(defaultFallback = "default_fallback")
@Slf4j
public class PaymentService {
@HystrixCommand
@GetMapping(value = "/normalTest")
public String normalTest() {
return Thread.currentThread().getName() + " => provider Normal Test";
}
@HystrixCommand
@GetMapping(value = "/timeoutTest")
public String timeoutTest() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
log.info("====Timout Exception ヾ(≧ ▽ ≦)ゝ");
}
return Thread.currentThread().getName() + " => provider Timeout Test";
}
@HystrixCommand
@GetMapping(value = "/exceptionTest")
public String exceptionTest() {
int i = 10 / 0;
return Thread.currentThread().getName() + " => provider Exception Test";
}
public String default_fallback() {
return Thread.currentThread().getName() + " => This is the provider default_fallback";
}
}
消费端-接口实现Service对Fallback统一处理(OpenFeign)
Service
在类上标识 @FeignClient 注解 value 指定微服务名,fallback指定处理服务降级的类
注意:这里的请求路径和服务提供者的Controller请求路径一致
import com.live.service.impl.PaymentServiceFallBack;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
@Component
@FeignClient(value = "hystrix-provider8001",fallback = PaymentServiceFallBack.class)
public interface PaymentService {
@GetMapping(value = "/provider/normal")
String normalTest();
@GetMapping(value = "/provider/timeout")
String timeoutTest();
@GetMapping(value = "/provider/exception")
String exceptionTest();
}
ImplFallback
实现Service接口,对方法进行降级处理;注意加上 @Component 注解加入Spring容器中
import com.live.service.PaymentService;
import org.springframework.stereotype.Component;
@Component
public class PaymentServiceFallBack implements PaymentService {
@Override
public String normalTest() {
return Thread.currentThread().getName() + " => This is the consumer Normal fall back";
}
@Override
public String timeoutTest() {
return Thread.currentThread().getName() + " => This is the consumer Timeout fall back";
}
@Override
public String exceptionTest() {
return Thread.currentThread().getName() + " => This is the consumer Exception fall back";
}
}
Controller
- Provider
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@RequestMapping(value = "/provider")
public class PaymentController {
@Resource
private PaymentService paymentService;
@GetMapping(value = "/normal")
public String normal() {
return paymentService.normalTest();
}
@GetMapping(value = "/timeout")
public String timeout() {
return paymentService.timeoutTest();
}
@GetMapping(value = "/exception")
public String exception() {
return paymentService.exceptionTest();
}
}
- Consumer
import com.live.service.PaymentService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@RequestMapping(value = "/consumer")
public class ConsumerController {
@Resource
private PaymentService paymentService;
@GetMapping(value = "/normal")
public String normal() {
return paymentService.normalTest();
}
@GetMapping(value = "/timeout")
public String timeout() {
return paymentService.timeoutTest();
}
@GetMapping(value = "/exception")
public String exception() {
return paymentService.exceptionTest();
}
}