1.断路器
2.熔断是什么
熔断理念的作者论文:https://martinfowler.com/bliki/CircuitBreaker.html
熔断机制概述:
熔断机制是应对雪崩效应的一种微服务链路保护机制。当扇出链路的某个微服务出错不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后,恢复调用链路。
在Spring Cloud框架里,熔断机制通过Hystrix实现。Hystrix会监控微服务之间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败,就会启动熔断机制,熔断机制的注解是@HystrixCommand。
请求熔断:请求熔断: 当Hystrix Command请求后端服务失败数量超过一定比例(默认50%), 断路器会切换到开路状态(Open). 这时所有请求会直接失败而不会发送到后端服务. 断路器保持在开路状态一段时间后(默认5秒), 自动切换到半开路状态(HALF-OPEN),这时会判断下一次请求的返回情况, 如果请求成功, 断路器切回闭路状态(CLOSED), 否则重新切换到开路状态(OPEN). Hystrix的断路器就像我们家庭电路中的保险丝, 一旦后端服务不可用, 断路器会直接切断请求链, 避免发送大量无效请求影响系统吞吐量, 并且断路器有自我检测并恢复的能力.
3.重要的配置
参数 | 描述 | 默认值 |
---|---|---|
circuitBreaker.enabled | 确定断路器是否用于跟踪运行状况和短路请求(如果跳闸)。 | 默认值为true |
circuitBreaker.requestVolumeThreshold | 熔断触发的最小个数/10s | 默认值:20 |
circuitBreaker.sleepWindowInMilliseconds | 熔断多少秒后去尝试请求 | 默认值:5000 |
circuitBreaker.errorThresholdPercentage | 失败率达到多少百分比后熔断 | 默认值:50 主要根据依赖重要性进行调整 |
circuitBreaker.forceOpen | 属性如果为真,强制断路器进入打开(跳闸)状态,其中它将拒绝所有请求。 | 默认值为false 此属性优先于circuitBreaker.forceClosed |
circuitBreaker.forceClosed | 该属性如果为真,则迫使断路器进入闭合状态,其中它将允许请求,而不考虑误差百分比。 | 默认值为false 如果是强依赖,应该设置为true circuitBreaker.forceOpen属性优先,因此如果forceOpen设置为true,此属性不执行任何操作。 |
4.开始使用熔断
4.1.修改cloud-provider-hystrix-payment8001
在PaymentService中新增如下内容:
在10次请求中失败率达到60%就切换到开路状态(Open),在10000毫秒后,尝试切换到半开路状态(HALF-OPEN)
//=====服务熔断
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback", commandProperties = {
// 确定断路器是否用于跟踪运行状况和短路请求(如果跳闸)。默认值为true
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
// 熔断触发的最小个数/10s,默认值:20
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
// 熔断多少毫秒后去尝试请求,默认值:5000
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),
// 失败率达到多少百分比后熔断,默认值:50,主要根据依赖重要性进行调整
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60"),
})
public String paymentCircuitBreaker(@PathVariable("id") Integer id) {
if (id < 0) {
throw new RuntimeException("******id 不能负数");
}
String serialNumber = IdUtil.simpleUUID();
return Thread.currentThread().getName() + "\t" + "调用成功,流水号: " + serialNumber;
}
public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id) {
return "id 不能负数,请稍后再试,/(ㄒoㄒ)/~~ id: " + id;
}
在PaymentController中增加如下内容:
//====服务熔断
@GetMapping("/payment/circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id) {
String result = paymentService.paymentCircuitBreaker(id);
log.info("****result: " + result);
return result;
}
4.2.测试
启动Eureka注册中心:cloud-eureka-server7001,cloud-eureka-server7002
启动服务提供者:cloud-provider-hystrix-payment8001
正常测试
浏览器输入:http://localhost:8001/payment/circuit/31
返回如下内容:
表示接口调用成功
hystrix-PaymentService-1 调用成功,流水号: 6ceac43cf0e944378de1d706c24c596f
异常测试
浏览器输入:http://localhost:8001/payment/circuit/-31
表示接口调用失败进行了服务降级
id 不能负数,请稍后再试,/(ㄒoㄒ)/~~ id: -31
我们发送十次异常请求: http://localhost:8001/payment/circuit/-31 请求。
id 不能负数,请稍后再试,/(ㄒoㄒ)/~~ id: -31
id 不能负数,请稍后再试,/(ㄒoㄒ)/~~ id: -31
id 不能负数,请稍后再试,/(ㄒoㄒ)/~~ id: -31
id 不能负数,请稍后再试,/(ㄒoㄒ)/~~ id: -31
id 不能负数,请稍后再试,/(ㄒoㄒ)/~~ id: -31
id 不能负数,请稍后再试,/(ㄒoㄒ)/~~ id: -31
id 不能负数,请稍后再试,/(ㄒoㄒ)/~~ id: -31
id 不能负数,请稍后再试,/(ㄒoㄒ)/~~ id: -31
id 不能负数,请稍后再试,/(ㄒoㄒ)/~~ id: -31
id 不能负数,请稍后再试,/(ㄒoㄒ)/~~ id: -31
在发送正常请求:http://localhost:8001/payment/circuit/31
可以发现正常请求也没有请求成功,说明上面发送的错误请求已经触发开路状态(Open)进行熔断了,在配置的circuitBreaker.sleepWindowInMilliseconds** = **10000毫秒中所有的请求都会进行服务降级处理。
id 不能负数,请稍后再试,/(ㄒoㄒ)/~~ id: 31
等10000毫秒(10秒)过去之后从开路状态(Open) —-> 半开路状态(HALF-OPEN)
在发送一次正常请求:http://localhost:8001/payment/circuit/31
可以看到服务请求成功了,此时状态已经从半开路状态(HALF-OPEN) —-> 闭路状态(CLOSED)
hystrix-PaymentService-10 调用成功,流水号: 18d444f54ef14997b16ec8d459f6e7ab
如果还是发送异常请求,此时状态会从半开路状态(HALF-OPEN) —-> 开路状态(Open)
5.原理(小总结)
这个简单的断路器避免了在电路断开时发出受保护的呼叫,但是当情况恢复正常时,将需要外部干预才能将其重置。对于建筑物中的电路断路器,这是一种合理的方法,但是对于软件断路器,我们可以让断路器本身检测基础调用是否再次正常工作。我们可以通过在适当的时间间隔后再次尝试受保护的调用来实现这种自我重置行为,并在成功后重置断路器。
创建这种中断器意味着要增加一个阈值,以尝试重设并设置一个变量来保存最后一次错误的时间。