今天来看下Hystrix的熔断与降级。
首先什么是降级?当请求超时、资源不足等情况发生时进行服务降级处理,不调用真实服务逻辑,而是使用快速失败(fallback)方式直接返回一个托底数据,保证服务链条的完整,避免服务雪崩。需要注意的是,服务降级是在客户端层面实现的。接下来通过代码进行一个实践:
首先需要添加Hystrix的依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
application.yml:
server:
port: 10090
spring:
application:
name: spring-cloud-hystrix-test
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:9090/eureka
fetch-registry: true
register-with-eureka: true
controller层代码实现:
@RestController
public class TestController {
@Autowired
TestService service;
@GetMapping("/hystrix/test")
public String helloHystrix() {
return service.test();
}
}
service层代码实现:
@Service
public class TestService {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@HystrixCommand(fallbackMethod = "myFallback")
public String test() {
ServiceInstance instance = discoveryClient.getInstances("spring-cloud-service-provider").get(0);
String url = "http://" + instance.getHost() + ":" + instance.getPort() + "/test";
return restTemplate.getForObject(url, String.class);
}
public String myFallback() {
return "fallback";
}
}
这里我们指定了myFallback()作为Fallback方法,我们通过浏览器访问一下这个服务试试看:
调用成功,因为现在我们的服务端并没有出现超时等需要进行降级处理的异常。为了验证降级我们对客户端以及服务端的代码进行微调。
客户端上增加了Hystrix属性中timeout的设置,调整为3秒钟未取到服务端的返回,视为超时:
@HystrixCommand(fallbackMethod = "myFallback", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
})
public String test() {
ServiceInstance instance = discoveryClient.getInstances("spring-cloud-service-provider").get(0);
String url = "http://" + instance.getHost() + ":" + instance.getPort() + "/test";
return restTemplate.getForObject(url, String.class);
}
服务端则是增加一个4秒中的sleep:
@RequestMapping(value = "/test", method = RequestMethod.GET)
public String test() throws InterruptedException {
Thread.sleep(4000);
return "Hello world!";
}
再次进行验证:
这次可以看到成功触发了降级。
再来看一下什么是熔断。当一定时间内,异常请求比例(请求超时、网络故障、服务异常等)达到阈值时,启动熔断器,熔断器一旦启动,则会停止调用具体服务逻辑,通过fallback快速返回托底数据,保证服务链的完整。看上去和降级差不多?不过熔断是在服务端实现,目的是当服务端的某个服务出现异常后为了不影响其他客户端的请求而做出的及时回应。
关于熔断,我们还有必要了解一下熔断机制的三个状态:关闭,开启和半开。最开始是关闭状态,这个时候所有请求都可以通过;如果错误请求达到一定的阈值,就会变成开启状态,此时所有请求短路,直接返回失败的响应;一段时间后,断路器会变成半开状态,如果下一个请求成功了,就关闭断路器,反之就开启断路器。
来看一下具体的代码实现:
@RestController
public class ServiceController {
@RequestMapping(value = "/test", method = RequestMethod.GET)
@HystrixCommand(fallbackMethod = "myFallback",
commandProperties = {
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD, value="1"),
@HystrixProperty(name=HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS, value="5000")
})
public String test() throws Exception {
System.out.println("test() called...");
throw new Exception("test exception");
}
private String myFallback() {
return "fallback";
}
}
这里设置了熔断的一个阈值,也就是10秒内异常请求数达到1次就进行熔断,同时在5秒钟后恢复请求状态。
启动两个浏览器进行验证:
可以看到由于服务端直接抛出异常,两次调用均调用了托底服务,但是服务端却只记录了一次调用,因为第一次调用抛出异常后已经进入熔断状态:
同时由于设置了5秒后恢复请求,我们在5秒后再次尝试调用,服务端又会重新记录正常调用时的信息: