在微服务常用技能之优雅重试这篇里面我们聊到了如何在调用服务发生异常的情况下进行重试(优雅的重试),但是我们也发现了一些问题,问题如下:
1、 当第三方服务宕机或者不可用,一个请求过来之后失败了3次,第二次、第三次请求都是失败3次,我们能不能直接失败,而不是重复调用不可用的服务呢
2、spring-retry中的@Recover只能是对异常进行事后处理,所有的@Retryable都可能会调用到此方法,我们有没有针对某一个服务的降级处理呢?
如果你也想了如上的问题或者你想到更多的问题,那么恭喜你,你的才能一定在我之上,并且现在或者将来一定大有可为,架构师指日可待,当上CEO、赢取白富美…
现在我们带着这些问题,我们来看看都有哪些解决方法
Spring Retry 注解@CircuitBreaker 实现
和微服务常用技能之优雅重试中的代码类似,maven导入的包也一样,修改如下代码
@Service
public class BookService {
private Random random = new Random();
// @Retryable(value = WebServiceException.class ,include = RuntimeException.class, maxAttempts = 3)
@CircuitBreaker(maxAttempts = 1, resetTimeout = 10000L, openTimeout = 5000L)
public String readingList(String str) {
int randomInt= random.nextInt(10) ;
if(randomInt < 8){ //模拟调用失败情况
System.out.println("random:" + randomInt
+" time:" + LocalTime.now());
throw new WebServiceException("call dependency service fail.");
}else{
System.out.println("调用成功" + str);
return "调用成功 ;number:" + randomInt;
}
}
@Recover
public String reliable() {
return "Cloud Native Java (O'Reilly)";
}
}
如上代码,注释掉@Retryable注解,改为@CircuitBreaker注解即可,为了测试方便maxAttempts 我设置为1是因为测试当失败一次则开始熔断开关,以后所有请求则不再调用此方法,resetTimeout 设置为10秒,默认是2秒,时间足够大,点击按钮才能感受到效果。
说明:
1、maxAttempts 表示重试次数,默认为3
2、resetTimeout 熔断开关打开后等待重置时间即何时恢复
3、openTimeout 表示在此设置的时间段内如果失败次数达到maxAttempts 设置的次数则熔断
注意(如果我说的不对,请指正):
如果在openTimeout 时间范围内失败次数达到maxAttempts 并且陆续会有请求进来的话,熔断开关将会一直处于打开状态也就是所有请求只能走@Recover方法
hystrix 实现
首先引入所需包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
代码部分
@SpringBootApplication
@EnableCircuitBreaker
public class HystrixNotesApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixNotesApplication.class, args);
}
}
@Service
public class BookService {
private Random random = new Random();
@HystrixCommand(fallbackMethod = "reliable",
commandProperties = {@HystrixProperty(name = EXECUTION_ISOLATION_STRATEGY, value = "SEMAPHORE")})
public String readingList(String str) {
int randomInt= random.nextInt(10) ;
if(randomInt < 8){ //模拟调用失败情况
System.out.println("调用失败发生异常" + str);
throw new WebServiceException("call dependency service fail.");
}else{
System.out.println("调用成功" + str);
return "调用成功 ;number:"+randomInt;
}
}
public String reliable(String str) {
System.out.println(str + "===");
// System.out.println(e.fillInStackTrace());
return "Cloud Native Java (O'Reilly)";
}
}
代码部分大概就如上这样了,主要就是在入口方法上面加上注解@EnableCircuitBreaker,在具体的服务上添加上@HystrixCommand注解
1、 fallbackMethod 方法指定了熔断时执行的方法,上面的指定的方法是reliable(String str),这个方法必须和@HystrixCommand注解所在类保持一致且参数也应该和readingList一样
2、HystrixCommand 在熔断方面有几个比较重要的配置参数,都HystrixCommandProperties.java里面了,我们先说下熔断的逻辑(过程)
熔断过程
1、假设通过断路器的量达到一定的阈值(默认20)
(HystrixCommandProperties.circuitBreakerRequestVolumeThreshold())…
2、并且假设错误的百分比达到设定的阈值百分比(默认50)
(HystrixCommandProperties.circuitBreakerErrorThresholdPercentage())…
3、这时候熔断开关从CLOSE 状态变为OPEN状态
4、当熔断开关打开的时候,将会阻断所有的请求
5、经过一段时间之后(默认5秒)
(HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds()),下一个请求将会被放行(这就是HALF-OPEN状态),如果这次请求失败了,熔断器将会恢复到OPEN 状态,如果这次请求成功了,熔断开关变为CLOSE状态,然后继续进行步骤1,循环往复。
写在最后
熔断咱们就先说到这里了,短短小文不足以完全说明白熔断,并且希望看到的朋友能够给予补充和指正,另外上面hystrix实现的熔断我们还留下了不少疑问,比如注解@HystrixCommand里面的fallbackMethod ,HystrixProperty里面的隔离策略(EXECUTION_ISOLATION_STRATEGY),隔离策略中信号量、线程大小应该怎么设置才合理等等
参考文档
[1] command & thread properties
[2] CircuitBreaker Properties
[3] CircuitBreaker - How it work