在微服务应用中,服务存在一定的依赖关系,如果某个目标服务调用慢或者有大量超时造成服务不可用,间接导致其他的依赖服务不可用,最严重的可能会阻塞整条依赖链,最终导致业务系统崩溃(又称雪崩效应)。
上述的问题将是本篇需要解决的问题。
断路器是一种开关设置,当某个服务单元发生故障之后,通过断路器的故障监控,向调用方返回一个符合预期的服务降级处理(fallback),而不是长时间的等待或者抛出调用方无法处理的异常,这样保证了服务调用方的线程不会长时间被占用,从而避免了故障在分布式系统的蔓延乃至崩溃。
fallback 相当于是降级操作。对于查询操作, 我们可以实现一个 fallback 方法, 当请求后端服务出现异常的时候, 可以使用 fallback 方法返回的值。 fallback 方法的返回值一般是设置的默认值或者来自缓存,告知后面的请求服务不可用了,不要再请求了。
相同:
目标一致:为了防止系统崩溃而实施的一种防御手段
表现形式一致:当请求目标在一定时间内无响应时,返回或执行默认响应内容
不同
触发条件不同:下游服务出现故障触发请求熔断。系统负荷超过阈值触发服务降级。
管理目标层次不同:请求熔断针对所有微服务。服务降级针对整个系统中的外围服务。
Spring Cloud Hystrix 实现了断路器、线程隔离等一系列服务保护功能。它是基于 Netflix 的开源框架 Hystrix 实现的,该框架的目的在于通过控制访问远程系统、服务和第三方库节点,从而对延迟和故障提供更强大的容错能力。
Hystrix 具备服务熔断、服务降级、线程和信号隔离、请求缓存、请求合并以及服务监控的能力。
现在的项目列表如下: 服务实例 端口 描述 common-api - 公用的 api,如:实体类 eureka-server 9000 注册中心(Eureka 服务端) goods-server 8081 商品服务(Eureka 客户端) goods-server-02 8082 商品服务(Eureka 客户端) goods-server-03 8083 商品服务(Eureka 客户端) order-server 8100 订单服务(Eureka 客户端) 在 order-server 项目中:
<!-- hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
我们来修改获取下订单的方法,在 placeOrder 方法上加 @HystrixCommand 注解:
@Service
public class OrderServiceImpl implements OrderService{
@Autowired
private RestTemplate restTemplate;
// @Autowired
// private GoodsServiceClient goodsServiceClient;
@HystrixCommand(fallbackMethod = "defaultByPlaceOrder")
@Override
public void placeOrder(Order order) throws Exception{
Result result = this.restTemplate.getForObject("http://GOODS/goods/goodsInfo/" + order.getGoodsId(), Result.class);
// Result result = this.goodsServiceClient.goodsInfo(order.getGoodsId());
if (result != null && result.getCode() == 200) {
System.out.println("=====下订单====");
System.out.println(result.getData());
} else {
System.out.println(result.getMsg());
}
}
public void defaultByPlaceOrder(Order order) {
System.out.println("商品服务系统异常");
}
}
当调用商品服务超时或出现异常时,Hystrix 会调用 @HystrixCommand 中指定的 fallbackMethod 方法获取返回值或执行异常处理。
注意:fallbackMethod 方法要求与正常方法有相同的入参和回参。
在启动类上添加 @EnableCircuitBreaker 注解:
@EnableCircuitBreaker
@EnableEurekaClient
@SpringBootApplication
public class OrderServerApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServerApplication.class, args);
}
}
我们首先演示没有开启熔断的功能,即先把上边的 @EnableCircuitBreaker 注解进行注释。 启动好所有项目,使用 Postman 请求 order-server 进行下单操作,运行结果如下:
当我们请求发送的 goodsId 的商品不存在,服务提供方抛会异常,调用方无法处理,因此只能展示图中的异常信息。
下面,我们再将 @EnableCircuitBreaker 注解的注释放开,运行结果如下:
从图中可知,虽然请求了一个 goodsId 不存在的商品,但是调用方(order-server)开启了熔断机制,执行默认方法,从而使接口能正常通信而不是抛出调用方不可处理的异常导致整个系统不能正常运行。
看到这里,或许会有读者产生一个疑问,如果类中定义 N 个方法,是不是意味着同时也要定义 N 个异常处理的方法呢,答案是否定的。
Hystrix 还提供了 @DefaultProperties 统一处理请求熔断,在该注解上设置 defaultFallback 属性值,即熔断开启后要执行的方法。
@Service
@DefaultProperties(defaultFallback = "defaultByHystrix")
public class OrderServiceImpl implements OrderService{
// @Autowired
// private RestTemplate restTemplate;
@Autowired
private GoodsServiceClient goodsServiceClient;
@HystrixCommand
@Override
public void placeOrder(Order order) throws Exception{
// Result result = this.restTemplate.getForObject("http://GOODS/goods/goodsInfo/" + order.getGoodsId(), Result.class);
Result result = this.goodsServiceClient.goodsInfo(order.getGoodsId());
if (result != null && result.getCode() == 200) {
System.out.println("=====下订单====");
System.out.println(result.getData());
} else {
System.out.println(result.getMsg());
}
}
public void defaultByHystrix() {
System.out.println("商品服务系统异常");
}
}
注意:defaultFallback 定义的方法必须是无参的。 源码下载