Loading... <div class="preview"> <div class="post-inser post box-shadow-wrap-normal"> <a href="http://www.tangsong.fun/index.php/Hystrix1.html" target="_blank" class="post_inser_a no-external-link no-underline-link"> <div class="inner-image bg" style="background-image: url(https://blog-picture01.oss-cn-shenzhen.aliyuncs.com/img/20210626175930.JPG);background-size: cover;"></div> <div class="inner-content" > <p class="inser-title">[Hystrix]原理及入门</p> <div class="inster-summary text-muted"> 服务保障有SpringCloud集成的 Hystrix(不再维护)、基于前者轻量级模块化的 Resilience4... </div> </div> </a> <!-- .inner-content #####--> </div> <!-- .post-inser ####--> </div> 根据Hystrix的官网学习,主要列出以下两个方案: --- ## 方案对比 **概述**:需要进行容错处理的方法为**静态SOA请求**,跟基于AOP代理的注解方式不兼容。 ### 方案一:通过 `@HystrixCommand`注解的形式。 **优点**: 可以把降级方法写在原先的类中 可共用触发熔断开关的设置(请求量、失败率、窗口期),配置通用。 **不足**: 注解通过AOP方式代理,无法对SoaServer中的静态方法进行容错,需要更改为普通方法。 ### 方案二:通过继承 `HystrixCommand`对每个需要容错的方法创建对应的类 **优点**: 可实现静态方法的容错 可结合ETCD来动态开启熔断开关 可对不同的方法进行不同的业务逻辑上的容错处理 可对每个方法触发熔断的设计(请求量、失败率、窗口期)进行不同配置 **不足**: 无法进行通用的降级处理 ## 代码演示 引入依赖,排除冲突 ```xml <!-- 引入 Hystrix 依赖 --> <dependency> <groupId>com.netflix.hystrix</groupId> <artifactId>hystrix-core</artifactId> <version>1.5.18</version> <exclusions> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.netflix.hystrix</groupId> <artifactId>hystrix-javanica</artifactId> <version>1.5.18</version> </dependency> ``` ### 方案一 #### 一、创建 `HystrixConfig` 配置类,构造 `HystrixCommandAspect` Bean。 HystrixCommandAspect 通过 `@Around()`形式,扫描 `@HystrixCommand` 和 `@HystrixCollapser` 注解的切面,从而使用 Hystrix 进行服务容错。 ```java @Configuration @EnableAspectJAutoProxy // 开启 AOP 代理的支持 public class HystrixConfig { @Bean public HystrixCommandAspect hystrixCommandAspect() { return new HystrixCommandAspect(); } } ``` #### 二、创建服务单元A ```java @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 ```java @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**上。 **结果** 控制台: ```java [getUserFallback][id(1) exception(HttpClientErrorException.NotFound: 404 : [<!doctype html><html lang="en"><head><title>HTTP Status 404 – Not Found</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} ... (431 bytes)])] ``` 客户端: ```json { "code":"0", "success":true, "msg":"", "data":"mock:User:1", "extra":"" } ``` #### 附:@HystrixCommand完整注解参数 1. `fallbackMethod` 属性: 指定 fallback 服务降级的处理方法,处理相应的异常。 2. `ignoreExceptions` 属性: 指定忽略指定的异常 Class,不进行 fallback 服务降级。 3. `commandKey` 属性: Hystrix Command 命令键,默认未配置情况下,使用方法名。例如说,我们上面的 #getUser(...) 对应的 commandKey 属性默认为 getUser。 4. `groupKey` 属性: Hystrix Command 命令分组键,用于 Hystrix 根据不同的分组来统计命令的统计、告警、仪表盘信息。<br>默认未配置情况下,使用方法所在类名。例如说,我们上面的 #getUser(...) 方法所在类为DemoController,则对应的 groupKey 属性默认为 DemoController。 5. `threadPoolKey` 属性 线程池名,用于划分不同的线程池,进行资源隔离。<br>默认未配置情况下,相同 groupKey 的 Hystrix Command 使用同一个线程池。在配置情况下,相同groupKey + threadPoolKey 使用同一个线程池。也就是说,groupKey 是必选的基础维度,而threadPoolKey 是可选的进一步细化维度。 --- ### 方案二 #### 一、构建Command去继承 `HystrixCommand` `run()`:原有业务逻辑 `getFallback()`:服务降级的回退逻辑 ```java 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 ```java @RestController @RequestMapping("/demo") public class DemoController { public static String sayUuid(String uuid){ int a = 1/0; return "say:" + uuid; } } ``` #### 三、创建服务单元B ```java @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` **结果:** ```json { "code":"0", "success":true, "msg":"test success: -1", "data":"", "extra":"" } ``` #### 附:@HystrixCommand注解/@HystrixCollapser 注解 ```java @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 ""; } ``` ```java @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 {}; } ``` Last modification:August 22, 2022 © Allow specification reprint Like 0 喵ฅฅ