Hystrix组件
Hystrix是一个用于处理分布式系统的延迟和容错的开源库。在分布式系统中,许多依赖不可避免的会调用失败,超时异常等。Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障(服务雪崩现象),提高分布式系统的健壮性。
Hystrix断路器原理
1. 原理
断路器是一个开关装置,有全开(OPEN),半开(HALF_OPEN),关闭(CLOSED)三种状态,默认处于CLOSED状态。
在服务调用的过程中,代理类维护了最近调用失败的次数。如果某次调用失败,则使失败次数加1。如果最近失败次数超过了在给定时间内允许失败的阈值,则代理类切换到OPEN状态。此时代理开启了一个超时时钟,当该时钟计时结束,自动切换到HALF_OPEN状态。该超时时间的设定是给了系统一次机会来修正导致调用失败的错误。
2. 三种状态
-
CLOSED:关闭状态。所有请求都正常访问。
-
OPEN:全开状态。每次开启有时间限制(默认5s),这时所有请求访问失败,自动触发相应的降级方法。
-
HALF_OPEN:半开状态。OPEN状态开启后,进入5s休眠计时,5s后自动进入HALF_OPEN状态。此时会释放1次请求通过,若这个请求是健康的,则会关闭断路器。否则继续保持打开,再次进行5秒休眠计时。
3. 断路器打开的条件
-
规定时间内请求次数达到一定的阀值的时候(默认10秒内超过20个请求次数)。
-
规定时间内请求次数的失败率达到一定的阀值时候(默认10秒内超过50%的请求失败)。
服务熔断的实现
服务熔断在服务端 (被调用方) 实现,由服务端对异常服务进行降级,返回错误信息。
1. 指定降级方法
-
被调用方引入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
-
被调用方开启断路器
@SpringBootApplication @EnableDiscoveryClient // Hystrix开启链路监控 @EnableCircuitBreaker public class ProductsApplication { public static void main(String[] args) { SpringApplication.run(ProductsApplication.class, args); } }
-
被调用方创建降级方法
/** * 指定降级方法,只有当绑定的被监控方法中出现异常,服务器线程资源耗尽时等触发熔断机制时被调用 * 参数必须和绑定的被监控方法一致 * @param id 绑定方法的参数 * @return 绑定方法出现异常时返回的fallback */ public Map<String, Object> findOneFallback(int id) { Map<String, Object> fallback = new HashMap<>(); fallback.put("status", false); fallback.put("msg", "findOne出现异常"); fallback.put("id", id); return fallback; }
-
被调用方给被监控的方法绑定降级方法
@GetMapping("/product/findOne") // 熔断策略 @HystrixCommand(fallbackMethod = "findOneFallback") public Map<String, Object> findOne(@RequestParam("id") int id) { // 如果传递的id小于0,出现异常,但是异常不会被抛出 // 同时断路器半开,熔断方法自动触发 if (id < 0) { throw new RuntimeException("id不能小于0"); } Map<String, Object> result = new HashMap<>(); result.put("status", true); result.put("msg", "商品"); result.put("id", id); return result; }
2. 默认降级方法
如果为每一个服务方法开发一个降级方法,对于我们来说,可能会出现大量的代码的冗余,不利于维护。这个时候就需要加入默认服务降级处理方法。
-
被调用方引入依赖,开启断路器。
-
被调用方创建默认降级方法
/** * 默认降级方法,可以与多个被监控的方法进行绑定 * @return 通用的fallback */ public Map<String, Object> defaultFallback() { Map<String, Object> fallback = new HashMap<>(); fallback.put("status", false); fallback.put("msg", "服务提供方出现异常"); return fallback; }
-
被调用方给多个方法绑定同一个降级方法
@PostMapping("/product/updateOne") // 熔断策略 @HystrixCommand(defaultFallback = "defaultFallback") public Map<String, Object> updateOne(@RequestBody Product product) { Map<String, Object> result = new HashMap<>(); result.put("status", true); result.put("msg", "修改商品"); result.put("product", product); return result; }
服务降级的实现
服务降级在客户端 (调用方) 实现,当某个服务熔断之后,服务器将不再被调用,此刻客户端可以自己准备一个本地的fallback回调,返回一个错误信息。
当前Spring Cloud处理方案中,一般使用OpenFeign + Hystrix解决服务降级。
-
调用方引入OpenFeign和Hystrix的依赖。
-
调用方application.yml开启OpenFeign对Hystrix的支持
feign: # feign对hystrix支持 hystrix: enabled: true
-
调用方创建fallback包。
-
创建Feign客户端对应的Fallback类,实现Feign客户端接口,并对客户端中的每一个方法分别做降级方法
@Component public class ProductClientFallback implements ProductClient { @Override public Map<String, Object> showProducts() { Map<String, Object> fallback = new HashMap<>(); fallback.put("states", false); fallback.put("msg", "showProducts服务关闭"); return fallback; } @Override public Map<String, Object> findOne(int id) { Map<String, Object> fallback = new HashMap<>(); fallback.put("states", false); fallback.put("msg", "findOne服务关闭"); return fallback; } @Override public Map<String, Object> updateOne(Product product) { Map<String, Object> fallback = new HashMap<>(); fallback.put("states", false); fallback.put("msg", "updateOne服务关闭"); return fallback; } }
-
将Feign客户端指向对应的Fallback类
// 注解中填写服务名和绑定的Fallback类 @FeignClient(value = "products", fallback = ProductClientFallback.class) public interface ProductClient { @GetMapping("/product/showProducts") Map<String, Object> showProducts(); @GetMapping("/product/findOne") Map<String, Object> findOne(@RequestParam("id") int id); @PostMapping("/product/updateOne") Map<String, Object> updateOne(@RequestBody Product product); }
注意:
-
在构建Feign客户端对应的Fallback类时,以下代码会报错。因为@Autowired是通过对象类型注入对象的,如果ProductClient接口下实现了多个类,那么@Autowired就不能准确的指定到底是哪一个实现类,从而报错。
@Autowired private ProductClient productClient;
解决方案:在Feign客户端上加@Primary,让其优先注入接口而不是它的实现类
// 解决注入ProductClient实例优先注入接口而不是实现类 @Primary // 注解中填写服务名和绑定的Fallback类 @FeignClient(value = "products", fallback = ProductClientFallback.class) public interface ProductClient { ... }
-
如果调用端和被调用端对于同一个方法都做了降级方法,那么两个降级方法的返回类型必须一致!
比如对于showProducts方法,在调用端中降级策略为:
@Override public Map<String, Object> showProducts() { Map<String, Object> fallback = new HashMap<>(); fallback.put("states", false); fallback.put("msg", "showProducts服务关闭"); return fallback; }
在被调用端中降级策略为:
public Map<String, Object> defaultFallback() { Map<String, Object> fallback = new HashMap<>(); fallback.put("status", false); fallback.put("msg", "服务提供方出现异常"); return fallback; }
降级方法的返回类型必须都是Map<String, Object>。
关于Hystrix Dashboard
Hystrix Dashboard的一个主要优点是它收集了关于每个HystrixCommand的一组度量。Hystrix仪表板以高效的方式显示每个断路器的运行状况。
由于Hystrix已经进入维护状态,Spring Cloud在F版本后适配的JQuery版本和Hystrix Dashboard中的JQuery版本不一致,导致Hystrix Dashboard无法正常使用。在新版G,H中已经基本不会使用Hystrix Dashboard。