[post cid="1225" cover="https://blog-picture01.oss-cn-shenzhen.aliyuncs.com/img/20210626175930.JPG"/]根据Hystrix的官网学习，主要列出以下两个方案：方案对比概述：需要进行容错处理的方法为静态SOA请求，跟基于AOP代理的注解方式不兼容。方案一：通过 @HystrixCommand注解的形式。优点：可以把降级方法写在原先的类中可共用触发熔断开关的设置（请求量、失败率、窗口期），配置通用。不足：注解通过AOP方式代理，无法对SoaServer中的静态方法进行容错，需要更改为普通方法。方案二：通过继承 HystrixCommand对每个需要容错的方法创建对应的类优点：可实现静态方法的容错可结合ETCD来动态开启熔断开关可对不同的方法进行不同的业务逻辑上的容错处理可对每个方法触发熔断的设计（请求量、失败率、窗口期）进行不同配置不足：无法进行通用的降级处理代码演示引入依赖，排除冲突 com.netflix.hystrix hystrix-core 1.5.18 commons-logging commons-logging com.netflix.hystrix hystrix-javanica 1.5.18 方案一一、创建 HystrixConfig 配置类，构造 HystrixCommandAspect Bean。HystrixCommandAspect 通过 @Around()形式，扫描 @HystrixCommand 和 @HystrixCollapser 注解的切面，从而使用 Hystrix 进行服务容错。@Configuration @EnableAspectJAutoProxy // 开启 AOP 代理的支持 public class HystrixConfig { @Bean public HystrixCommandAspect hystrixCommandAspect() { return new HystrixCommandAspect(); } }二、创建服务单元A@RestController @RequestMapping("/demo") public class DemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private RestTemplate restTemplate; @GetMapping("/get_user") @HystrixCommand(fallbackMethod = "getUserFallback") public String getUser(@RequestParam("id") Integer id) { logger.info("[getUser][准备调用 user-service 获取用户({})详情]", id); return restTemplate.getForEntity("http://127.0.0.1:12580/user/get?id=" + id, String.class).getBody(); } public String getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, ExceptionUtils.getRootCauseMessage(throwable)); return "mock:User:" + id; } }三、创建服务单元B@RestController @RequestMapping("/user") public class UserController { @RequestMapping("/get") public JsonResult getUser(@RequestParam("id") Integer id) { return JsonResult.success("user:" + id); } }四、测试通过 http://127.0.0.1:8080/demo/get_user?id=1请求 getUser()方法，该方法打到一个错误的url上。结果控制台：[getUserFallback][id(1) exception(HttpClientErrorException.NotFound: 404 : [ HTTP Status 404 – Not Found body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} ... (431 bytes)])]客户端：{ "code":"0", "success":true, "msg":"", "data":"mock:User:1", "extra":"" }附：@HystrixCommand完整注解参数fallbackMethod 属性： 指定 fallback 服务降级的处理方法，处理相应的异常。ignoreExceptions 属性： 指定忽略指定的异常 Class，不进行 fallback 服务降级。commandKey 属性： Hystrix Command 命令键，默认未配置情况下，使用方法名。例如说，我们上面的 #getUser(...) 对应的 commandKey 属性默认为 getUser。groupKey 属性： Hystrix Command 命令分组键，用于 Hystrix 根据不同的分组来统计命令的统计、告警、仪表盘信息。默认未配置情况下，使用方法所在类名。例如说，我们上面的 #getUser(...) 方法所在类为DemoController，则对应的 groupKey 属性默认为 DemoController。threadPoolKey 属性 线程池名，用于划分不同的线程池，进行资源隔离。默认未配置情况下，相同 groupKey 的 Hystrix Command 使用同一个线程池。在配置情况下，相同groupKey + threadPoolKey 使用同一个线程池。也就是说，groupKey 是必选的基础维度，而threadPoolKey 是可选的进一步细化维度。方案二一、构建Command去继承 HystrixCommandrun():原有业务逻辑getFallback():服务降级的回退逻辑public class GetUserCommand extends HystrixCommand<Integer> { private final static Logger logger = LoggerFactory.getLogger(GetUserCommand.class); public GetUserCommand() { super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("orderService")) .andCommandKey(HystrixCommandKey.Factory.asKey("queryByOrderId")) .andCommandPropertiesDefaults(HystrixCommandProperties.Setter() .withCircuitBreakerRequestVolumeThreshold(10)//至少有10个请求，熔断器才进行错误率的计算 .withCircuitBreakerSleepWindowInMilliseconds(5000)//熔断器中断请求5秒后会进入半打开状态,放部分流量过去重试 .withCircuitBreakerErrorThresholdPercentage(50)//错误率达到50开启熔断保护 .withExecutionTimeoutEnabled(true)) .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withCoreSize(10))); } @Override protected Integer run() { System.out.println(DemoController.sayUuid("123")); return 1; } @Override protected Integer getFallback() { return -1; } }二、创建服务单元A@RestController @RequestMapping("/demo") public class DemoController { public static String sayUuid(String uuid){ int a = 1/0; return "say:" + uuid; } }三、创建服务单元B@RestController @RequestMapping("/user") public class UserController { @RequestMapping("/test") public JsonResult test() throws ExecutionException, InterruptedException { // int i = new GetUserCommand().execute(); Future<Integer> queue = new GetUserCommand().queue(); int i = queue.get(); return JsonResult.success("test success: " + i); } }四、测试通过 http://127.0.0.1:8080/user/test请求test()方法，执行了GetUserCommand中的run()，在run()中调用了静态方法DemoController.sayUuid()。里面引发了除0异常，从而触发了Fallback()返回 -1结果：{ "code":"0", "success":true, "msg":"test success: -1", "data":"", "extra":"" }附：@HystrixCommand注解/@HystrixCollapser 注解@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface HystrixCommand { //组 的名称 默认是 注解的方法所在类 的类名 String groupKey() default ""; //命令名称 默认就是 @HystrixCommand 所在的方法名 String commandKey() default ""; // 线程池名称，主要是来表示 HystrixThreadPool 监控，收集等 String threadPoolKey() default ""; /**回调方法，就是 失败或者超时时的方法 这个方法必须和 @HystrixCommand 注释的方法在同一个类里面 并且有一样的入参和出参 */ String fallbackMethod() default ""; // 配置相关的参数 ，以Key-Value 的形式,可配置多个，是数组形式 HystrixProperty[] commandProperties() default {}; //线程池的属性配置，也是key-value形式，可配置多个 HystrixProperty[] threadPoolProperties() default {}; // 定义哪些异常可以忽略 Class<? extends Throwable>[] ignoreExceptions() default {}; /** *定义 用哪种模式 运行hystrix observable command. * eager - {@link HystrixObservable#observe()}, * lazy - {@link HystrixObservable#toObservable()}. */ ObservableExecutionMode observableExecutionMode() default ObservableExecutionMode.EAGER; HystrixException[] raiseHystrixExceptions() default {}; /* 默认的回退方法，如果fallbackMethod 和defaultFallback 同时指定了，fallbackMethod优先 默认的回退方法 不能有入参，出参需要保持一致 */ String defaultFallback() default ""; }@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface HystrixCollapser { //合并名称，默认是 注解的方法名称 String collapserKey() default ""; /* 合并的方法名称 合并的方法必须 要是这种特征： java.util.List method(java.util.List) 合并的方法只能有一个 参数 */ String batchMethod(); /** 范围， 默认是一次请求，可以改成 GLOBAL 全局的 */ Scope scope() default Scope.REQUEST; /** * 合并的相关属性配置 * 以 key-value 形式配置，可以配置多个，数组 */ HystrixProperty[] collapserProperties() default {}; }
服务保障有SpringCloud集成的 Hystrix（不再维护）、基于前者轻量级模块化的 Resilience4j、以及阿里开源的 Sentinel。本篇主要以Hystrix进行服务保障的学习。[...]