FCon7折倒计时最后一周:日程已上线70%!查看详情>>> 了解详情
写点什么

Hystrix 在项目中实践

  • 2019-09-26
  • 本文字数:11974 字

    阅读完需:约 39 分钟

Hystrix在项目中实践

1 背景

我们的业务通常会大量依赖于第三方提供的服务,这些服务都是基于 Http 请求,然后依赖方的服务能力又是参次不齐的,因此,第三方资源的稳定性会直接影响着我们系统的稳定性。简单的调用模式如图 1 表示:



图 1 调用关系时序图


当某一个依赖的外部服务发生了异常,如果外部服务可以快速失败这到还好,最怕的就是服务响应变慢。可以试想一下,一个 http 请求过去,对方的服务一直在慢慢的进行处理,一直到我们所设置的 http 处理时间达到我们所设置的超时上限。在这个过程当中,很有可能会因为某一个外部资源响应速度变慢,而会长时间占用我们的 http 连接池,导致其他可以正常提供服务的资源也无法正常运行,严重的情况下甚至会产生雪崩效应。


如图 2 所示:当依赖的服务 F 发生了严重超时的情况时,所有与服务 F 相关的请求都会堵塞在这里。当后续第 N 个请求来的时候,即使与服务 F 没有依赖关系,但由于前面的请求把资源都占用了,所有其仍然拿不到资源,无法调用外部服务。



图 2 资源耗尽 无法响应

2 为什么引入 Hystrix

Hystrix 是用于分布式场景下服务熔断、降级的开源 Java 库。它的主要作用有线程隔离,熔断,降级和监控报警。这里不会过多的介绍 Hystrix 本身的概念及定义,具体内容可以看其 github 网站或是自行搜索,会有大量的仔细介绍。根据自身业务的具体情况,把外部依赖进行隔离,从而保证某些外部服务出现问题时,其它的服务仍然是可用并且是不受影响的。如图 3 所示:服务 F 发生异常,导致请求 3 无法处理请求,但是请求 4、5、6 仍然可以继续对外提供服务。



图 3 线程池隔离

3 模拟不引入 Hystrix 时

当模拟不引入 Hystrix 时,线程耗尽的情况:


  • 模拟应用调用第三方服务(第三方是我们模拟的一个 10 秒睡眠的服务)


1  /**2   * controller层3   * @param sleep 睡眠时间,模拟第三方业务的处理时间。4   */5  @RequestMapping(value = "hystrixTest")6  public ApiResult<String> hystrixTest(@RequestParam("sleep") int sleep){7          ApiResult<String> apiResult =iHystrixService.execute();8          return apiResult;9   }
复制代码


 1  /** 2   *service层 3   *service层中调用的/hystrix/sleep地址,就是我们模拟的第三方服务。 4   *@param sleep 睡眠时间,模拟第三方业务的处理时间。 5   *hystrix/sleep地址只是根据传过来的sleep参数进行Thread.sleep()操作。 6   */ 7  public ApiResult<String> execute(int sleep) { 8    try{ 9      System.out.println("开始业务处理 调用Http"10             + FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss")11             .format(new Date()));1213       // 模拟调用外部服务 逻辑仅是进行Tread.sleep操作模拟处理时间14       ResponseEntity<String> responseEntity = restTemplate15                      .getForEntity("http://127.0.0.1:8080/hystrix/sleep?sleep="16                  +sleep,String.class);1718       System.out.println("调用Http完成 satusCode:"19             +responseEntity.getStatusCode()+" "20             + FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss")21             .format(new Date()));2223       return ApiResult.SUCCESS();24    }25    catch (Exception e){26      return ApiResult.FAILED();27    }28}
复制代码


  • 配置线程池数量(这里配置成 5 个)


 1  <!-- 连接池管理器 HttpClient是线程安全的 且可同时执行多个线程的请求  2       管理持久的http连接 节省创建连接时间 --> 3  <!-- 维护的连接数在每个路由基础和总数上都有限制 --> 4  <bean id="pollingConnectionManager" class="org.apache.http.impl.conn 5                                  .PoolingHttpClientConnectionManager"> 6        <!-- 最大连接数 --> 7        <property name="maxTotal" value="5"/> 8        <!-- 每个路由基础的连接 --> 9        <property name="defaultMaxPerRoute" value="5"/>10  </bean>
复制代码


  • 模拟客户端服务调用(这里用的是 6 个)


我们模拟 6 个并发调用 controller,也就会发起 6 次服务调用,由于我们用 sleep 简单模拟的第三方服务,因此在我们发起第 6 次 controller 请求时,前面的 5 个请求均没有处理完毕返回结果,所以第 6 次的请求会因为拿不到 http 的连接无法调用 hystrix/sleep 这个模拟服务的地址。


从图 3 中我们也可以看出来,有 5 个请求因为拿到了 http 连接资源,可以正常访问外部服务,而有一个则因为当时 http 连接已被耗尽,则不能继续访问第三方服务。日志如图 4 所示:



图 4 有 1 个请求拿不到 http 连接资源


我们可以试想这样一种场景,刚刚 6 个请求,其对应的是 A,B 两个外部服务。假如 A 服务由于某种情况发生响应变慢,而 B 仍然能良好的提供服务,在这种情况下,就会发生由于 A 服务发生问题,而恰巧依赖 A 服务的外部接口又有大量请求,此时就会使得业务系统的资源全部被 A 服务占用,这样业务系统中与 A 服务无关的业务,也无法拿到资源去访问服务良好的 B 服务,最坏的情况就是业务系统中的一台机器被耗尽无法对外提供服务而 down 倒,由于负载均衡或是服务发现的机制,接下来所有的请求又会打到另外的机器上,一直到所有机器都被打爆,发生雪崩效应。


所以我们需要一种机制能够隔离坏的资源,让正常的资源提供正常的服务。这里我们就用到了上面说到的 Hystrix。


本文会介绍 hystrix 的两种使用方式,命令行编码和注解方式。

4Hystrix 的引用

  • 引入 maven 依赖


 1  <hystrix.core.version>1.5.12</hystrix.core.version> 2  <hystrix.javanica.version>1.5.12</hystrix.javanica.version> 3  <hystrix.metrics.version>1.5.12</hystrix.metrics.version> 4  <dependency> 5      <groupId>com.netflix.hystrix</groupId> 6      <artifactId>hystrix-core</artifactId> 7      <version>${hystrix.core.version}</version> 8  </dependency> 9  <dependency>10      <groupId>com.netflix.hystrix</groupId>11      <artifactId>hystrix-metrics-event-stream</artifactId>12      <version>${hystrix.metrics.version}</version>13   </dependency>14   <dependency>15       <groupId>com.netflix.hystrix</groupId>16       <artifactId>hystrix-javanica</artifactId>17       <version>${hystrix.javanica.version}</version>18   </dependency>
复制代码

5 注解方式使用 Hystrix

5.1 线程池隔离

业务层增加 @HystrixCommand 注解:


 1  @HystrixCommand(groupKey = "HystrixServiceImpl", // groupKey 该命令属于哪个组 2              commandKey = "execute", // 该命令的名称 3              threadPoolKey = "HystrixServiceImpl", // 该命令所属线程池的名称,默认同groupKey 4              fallbackMethod = "executeFallback", // 服务降级方法 5              commandProperties = { 6              @HystrixProperty(name = "execution.isolation.strategy", 7                                                   value = "THREAD"), // 线程池方式 8              @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", 9                                                    value = "5000")   // 超时时间为5秒10  })11  public ApiResult<String> execute() {12     try{13          System.out.println("开始业务处理 调用Http"14                + FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss")15                  .format(new Date()));1617          ResponseEntity<String> responseEntity = restTemplate18                  .getForEntity("http://127.0.0.1:8080/hystrix/sleep",String.class);1920          System.out.println("调用Http完成 satusCode:"21                + responseEntity.getStatusCode()+" "22                + FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss")23                  .format(new Date()));2425          return ApiResult.SUCCESS();26      }27      catch (Exception e){28          return ApiResult.FAILED();29      }30  }
复制代码


  • 增加回退方法


1  public ApiResult<String> executeFallback() {2       System.out.println(Thread.currentThread().getId()3              + " "+Thread.currentThread().getName()+" "4              + Thread.currentThread().getState().toString());56      return ApiResult.FAILED("hystrix->executeFallback");7  }
复制代码


  • 模拟超时


模拟第三方执行时间为 10S,而我们在 Hystrix 中设置的为 5S,所以会触发服务降级,应用在超时后执行 executeFallback 降级方法。如图 5 所示:



图 5 服务超时降级


细心点的人可以看出来,虽然客户端返回以及服务端执行了服务降级方法,但是原有的处理逻辑仍然继续执行,对于降级的请求,原有本身处理过程不会被中断。当服务降级到达一定的阀值时会启动断路器,后续一段时间内的请求都将进入降级方法,不会再调用业务逻辑。


  • 使用 Hystrix 进行线程隔离


新增加一个外部模拟服务 B,用于模拟访问不同的外部依赖, 分别设置其线程池大小为 2 个大小。


 1  @HystrixCommand(groupKey = "HystrixServiceImpl",commandKey = "execute", 2        fallbackMethod = "executeFallback", 3        commandProperties = { 4        @HystrixProperty(name = "execution.isolation.strategy",value = "THREAD"), 5        @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", 6                         value = "5000") 7        }, 8        threadPoolProperties = { 9        @HystrixProperty(name = "coreSize",value = "2"),// 线程池大小10        @HystrixProperty(name = "maQueueSize",value = "-1")// -1 表示不等待直接拒绝11  })
复制代码


  • 模拟 4 个客户端访问系统资源,其中 3 个访问 A 资源 1 个访问 B 资源。如图 6 所示:



图 6 4 个客户端请求 AB 两个资源


从日志可以看出,访问 A 资源的 3 个客户端,有 2 个成功,1 个返回 fallback 结果,这是因为我们设置的 Hystrix 的线程池大小为 2,所以第三个请求来的时候获取不到可用的线程池资源。而总的 restTemplate 线程池之前设置的为 5,此时仍有连接资源,所以 B 资源可以继续访问,而 A 由于业务处理的慢则没有连接返回释放资源,所以 A 的后继请求都是失败。


通过这种方式,可以起到线程隔离的效果,但是当所有的线程隔离池中当前所使用的总线程数大于了我们设置的 http 连接总数,总体业务当然还是失败的。Hystrix 只是起到了线程池资源隔离的作用,所以具体每个线程池设置多大的参数,还是要经过开发人员线上真实情况动态调整。

5.2 并发量

Hystrix 除了支持线程池外,还支持信号量方式控制请求并发数。


 1// 修改HystrixCommand为信息量方式 2@HystrixCommand(groupKey = "HystrixServiceImpl", 3                commandKey = "execute", 4                fallbackMethod = "executeFallback", 5      commandProperties = { 6      @HystrixProperty(name = "execution.isolation.strategy" 7                       ,value = "SEMAPHORE"), // 信号量方式 8      @HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests" 9                       ,value = "3") // 最大并发数为310})
复制代码


客户端模拟 4 个并发请求,只有 3 个是正常返回业务结果,另一个超过了并发数量限量触发了 fallback 方法。如下图 7:



图 7 并发日志

5.3 小结

通过上面的例子,可以了解到使用注解的方式使用 Hystrix 简单,易用并且开发量小。但其也有一些缺点,在实际应用当中,我们需要随业务的具体情况而动态的调整 Hystrix 参数,尤其是超时时间和并发数数量这些重要参数。另外,如果是对老业务的改造,使用注解方式需要把原有的所有外部依赖的地方都修改一下,其工作量也是不小。

6 命令式编辑使用 Hystrix

6.1 使用方式

上面介绍了在项目中使用 Hystrix 注解的方式对服务进行隔离和降级的注解方式。虽然简单、方便、易与使用,但是对于老业务来讲,把所有的方法都要加上 Hystrix 注解的话也不太友好,而且也不算灵活,无法做到动态修改。因此,想到使用 AOP 的方式,通过命令式编程对目标方式进行拦截,对需要使用服务降级的方法进行 Hystrix 操作,完全基于配置,这样就无需对原有的业务代码进行改动。


  • 服务降价配置说明


 1  # 对offerService.ocrIdCardImg方法进行降级配置 true:启用hystrix降级逻辑 false:不受hystrix控制 2  hystrix.method.com.ke.life.thirdpart.service.offerService.ocrIdCardImg=true 3  # 对offerService.ocrIdCardImg方法设置服务超时时间 4  hystrix.timeout.com.ke.life.thirdpart.service.offerService.ocrIdCardImg=1000 5  # 当使信号量时,对offerService.ocrIdCardImg进行并发数设置 6  hystrix.requestSize.com.ke.life.thirdpart.service.offerService.ocrIdCardImg=50 7  # 并发策略  SEMAPHORE或THREAD 也可以不配置默认为线程池 8  hystrix.isolationStrategy.com.ke.life.thirdpart.service.offerService.ocrIdCardImg=THREAD 9  # offerService.ocrIdCardImg触发hystrix服务降级时,返回的降级数据 void方法可以不配置10  hystrix.default.return.com.ke.life.thirdpart.service.offerService.ocrIdCardImg={"errno":"-1","msg":"服务降级"}
复制代码


此外,我们还有一些是业务的开关,在代码中是通过 ISystemSettingService 接口逻辑业务开关,代码中会有说明。


  • Hystrix 拦截器代码结构说明


 1  @Component 2  @Aspect 3  public class HystrixCommandAdvice { 4 5      private static final Logger logger = LoggerFactory.getLogger(HystrixCommandAdvice.class); 6 7      // 服务降级方法配置前缀 8      private static String HYSTRIX_METHOD_PREFIX="hystrix.method."; 910      // 服务降级方法超时时间配置前缀11      private static String HYSTRIX_TIMEOUT_PREFIX="hystrix.timeout.";1213      // 服务降级方法并发数配置前缀14      private static String HYSTIRX_REQUEST_SIZE_PREFIX="hystrix.requestSize.";1516      // 服务降级方法策略配置前缀 使用信号量还是线程池17      private static String HYSTRIX_ISOLATION_STRATEGE_PREFIX = "hystrix.isolationStrategy.";1819      // 服务降级方法返回类型配置前缀20      private static String HYSTRIX_DEFAULT_RETURN_PREFIX="hystrix.default.return.";2122      // iSystemSettingService获取业务中的配置,可以来自数据库、redis以及配置中心23      @Autowired24      private ISystemSettingService iSystemSettingService;2526      // 定义切点,拦截某些特定第三方服务27      @Pointcut("execution(* com.ke.life.thirdpart.service.*.*(..))")28      public void hystrixPointcut(){}2930      /**31       * 切面32       */33      @Around("hystrixPointcut()")34      public Object runCommand(final ProceedingJoinPoint pJoinPoint)  throws Throwable {35          // 此处省略,下面有具体代码说明36      }3738      /**39       *  Hystrix参数设置40       */41      private HystrixCommand.Setter setter(String commonGroupKey,String commandKey,42                                           int timeout,int requestSize,43                                           String strategy) {44           // 此处省略,下面有具体代码说明45      }4647      /**48       * 生成降级后的返回内容49       */50      public static Object generateClass(String clazzPackage , String defaultValue) 51                                                                  throws Exception{52          // 此处省略,下面有具体代码说明53  }
复制代码


  • runCommand 方法代码


 1  @Around("hystrixPointcut()") 2  public Object runCommand(final ProceedingJoinPoint pJoinPoint) throws Throwable { 3 4     // 获取服务签名 public xxxx.returnObject.class. com.xxxx.serice.method() 5     String signature = pJoinPoint.getSignature().toLongString(); 6     String serviceMethod = signature.substring(signature.lastIndexOf(" ")+1, 7              signature.lastIndexOf("(")); 8     boolean hystrixServiceBoolean = DynamicPropertyFactory.getInstance() 9          .getBooleanProperty(HYSTRIX_METHOD_PREFIX+serviceMethod, false).get();1011     // 降级开关打开状态并且配置此业务方法的降级方案12     // iSystemSettingService为我们自己定义的字典表,保存业务的开关和配置信息 13     if(true == hystrixServiceBoolean 14            && "true".equals(iSystemSettingService.getByKey("hystrix.switch"))){15            // 使用Hystrix服务降级封装的方法,具体代码下面有示例16            return wrapWithHystrixCommnad(pJoinPoint).execute();17     }18     // 没有配置此业务方法的降级方案,则直接执行19     try {20         return pJoinPoint.proceed();21     } catch (Throwable throwable) {22         throw (Exception) throwable;23     }24  }
复制代码


  • 服务方法封装 Hystirx 功能,wrapWithHystrixCommnad 代码


 1  // 使用hystrix封装业务执行的方法 2  private HystrixCommand<Object> wrapWithHystrixCommnad(final ProceedingJoinPoint  3                                                                       pJoinPoint) { 4          // 获取降级服务的方法签名  5          //如:public xxxx.return.class com.xxxx.serice.method() 6          String signature = pJoinPoint.getSignature().toLongString(); 7          // 解析出方法签名中返回的类型 如:xxxx.return.class 8          String returnClass = signature.substring(signature.indexOf(" ")+1 9                                                   ,signature.lastIndexOf(" "));10          // 解析出就去签名中的方法 如:com.xxxx.serice.method11          String serviceMethod = signature.substring(signature.lastIndexOf(" ")+112                                                    ,signature.lastIndexOf("("));1314          // 获取配置文件中的配置数据,6.2章节中有具体说明15          DynamicPropertyFactory dynamicPropertyFactory = DynamicPropertyFactory16                                                          .getInstance();17          // 超时时间 默认5秒18          int timeout = dynamicPropertyFactory19          .getIntProperty(HYSTRIX_TIMEOUT_PREFIX+serviceMethod, 5000).get();2021          // 返回值类型 默认空即void22          String defaultReturn = dynamicPropertyFactory23          .getStringProperty(HYSTRIX_DEFAULT_RETURN_PREFIX+serviceMethod,"").get();2425          // 请求数大小 默认50个 只有在策略使用信号量时再会用到26          int  requestSize= dynamicPropertyFactory27          .getIntProperty(HYSTIRX_REQUEST_SIZE_PREFIX+serviceMethod, 50).get();2829          // 隔离策略30          String strategy= dynamicPropertyFactory31          .getStringProperty(HYSTRIX_ISOLATION_STRATEGE_PREFIX+serviceMethod, "THREAD")32          .get();33          String declaringTypeName = pJoinPoint.getSignature()34          .getDeclaringTypeName();35          String commandKey = pJoinPoint.getSignature().getName();36          String commonGroupKey = declaringTypeName37          .substring(declaringTypeName.lastIndexOf(".")+1)+"."+commandKey;3839          return new HystrixCommand<Object>(setter(commonGroupKey,commandKey40              ,timeout,requestSize,strategy)) {4142              @Override43              protected Object run() throws Exception {44                  try {45                      Object object  = pJoinPoint.proceed();46                      return object;47                  } catch (Throwable throwable) {48                      throw (Exception) throwable;49                  }50              }51              /**52               * 以下四种情况将触发getFallback调用:53               * 1)run()方法抛出非HystrixBadRequestException异常54               * 2)run()方法调用超时55               * 3)断路器开启拦截调用56               * 4)线程池/队列/信号量是否跑满57               * 实现getFallback()后,执行命令时遇到以上4种情况将被fallback接管,58               * 不会抛出异常或其他59               */60              @Override61              protected Object getFallback() {6263                logger.info("服务触发了降级 {} 超时时间 {} 方法返回类型 {} 是否启动了熔断 {}" 64                    ,serviceMethod,timeout,returnClass,this.isCircuitBreakerOpen());65                try{66                     return generateClass(returnClass,defaultReturn);67                }68                catch (Exception e){69                    logger.error("生成降级返回对象异常",e);70                }71                return  null;72              }73          };74      }
复制代码


  • Hystrix 参数设置(AOP 方式同样也可以设置为线程池方式和信号量方式),setter 方法


 1  // hystrix参数设置 2  private HystrixCommand.Setter setter(String commonGroupKey,String commandKey 3          ,int timeout,int requestSize,String strategy) { 4    // 使用信号量 5    if("SEMAPHORE".equalsIgnoreCase(strategy)){ 6        return HystrixCommand.Setter 7               .withGroupKey(HystrixCommandGroupKey.Factory 8                   .asKey(commonGroupKey==null?"commonGroupKey":commonGroupKey)) 9                   .andCommandKey(HystrixCommandKey.Factory10                   .asKey(commandKey==null?"commandKey":commandKey))11                   // 信号量12                   .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()13                   .withExecutionIsolationStrategy(HystrixCommandProperties14                   .ExecutionIsolationStrategy.SEMAPHORE)15                   // 并发数量16                   .withExecutionIsolationSemaphoreMaxConcurrentRequests(requestSize)17                   .withExecutionTimeoutInMilliseconds(timeout));18     }19     else {20        // 使用线程池21        return HystrixCommand.Setter22               .withGroupKey(HystrixCommandGroupKey.Factory23                    .asKey(commonGroupKey==null?"commonGroupKey":commonGroupKey))24                    .andCommandKey(HystrixCommandKey.Factory25                    .asKey(commandKey==null?"commandKey":commandKey))26                    // 线程池大小27                    .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter()28                                                    .withCoreSize(requestSize))29                    .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()30                    // 线程池31                    .withExecutionIsolationStrategy(HystrixCommandProperties32                                                   .ExecutionIsolationStrategy.THREAD)33                    // 超时时间34                    .withExecutionTimeoutInMilliseconds(timeout)35                    .withExecutionIsolationThreadInterruptOnTimeout(true)36                    .withExecutionTimeoutEnabled(true));37          }38      }
复制代码


  • 生成降级返回的数据,generateClass 方法


 1  /** 2   * @param clazzPackage 返回的类 3   * @param defaultValue 降级后返回的值 4   */ 5  public static Object generateClass(String clazzPackage , String defaultValue) throws  6                                                                           Exception{ 7 8      if(StringUtils.isBlank(clazzPackage) || StringUtils.isBlank(defaultValue)){ 9              return null;10      }1112      logger.info("返回的类"+ clazzPackage);13      // 返回的类型是String类型的14      if(clazzPackage.contains("String")){15           String returnStr = new String(defaultValue.getBytes("iso8859-1"),"UTF-8");16           logger.info("降级返回的字符串 {}",returnStr);17           return returnStr;18      }1920      JSONObject hystrixReturnValueJson = JSONObject.parseObject(defaultValue);21      Iterator iterator = hystrixReturnValueJson.keySet().iterator();22      HashMap<String,String> fieldMap = new HashMap<>();23      while(iterator.hasNext()){24          String key = (String)iterator.next();25          String value = new String(hystrixReturnValueJson.getString(key)26                                    .getBytes("iso8859-1"),"UTF-8");27          logger.info("字段属性"+ key +"  "+value);28          fieldMap.put(key,value);29      }3031      // 使用反射,生成降级后返回的对象32      Class clazz = Class.forName(clazzPackage);33      Object object = clazz.newInstance();34      BeanUtils.populate(object,fieldMap);3536      logger.info("生成的降级返回类 {} {}",object.toString(),JSON.toJSONString(object));37      return object;38   }
复制代码

6.2. 动态修改配置

Hystrix 的超时时间,线程池大小以及并发数,都需要针对不对的场景和情况进行动态调整,所以动态配置是非常有必要的。例如:服务是从网关层统一转发到后端的业务应用上,那么网关层做动态限流就很有必要。随着后端业务机器及能力的变化可以在不重启网关服务的情况下随时动态修改网关的流量配置。


携程的 Apollo 和阿里的 Diamond 都是比较好的动态配置管理工具,界面也对用户友好。本文演示使用的是 netflix 的 Archaius。


Archaius 默认会自动读取项目类路径下的 config.properties 文件并且每分钟去重新加载下属性配置。


maven 引入 archaius 包


1   <dependency>2    <groupId>com.netflix.archaius</groupId>3    <artifactId>archaius-core</artifactId>4    <version>0.6.0</version>5   </dependency>
复制代码


  • 使用方式


在上面的示例代码中,其实就已经使用到了 archaius,其使用方式也是非常的简单


1  DynamicPropertyFactory dynamicPropertyFactory = DynamicPropertyFactory.getInstance();2  // 获取config.properties属性文件中key为hystrix.timeout的值。如果取不到,则使用默认值3  int value = dynamicPropertyFactory.getIntProperty("hystrix.timeout", 5000).get();
复制代码

6.3 小结

使用 AOP 的方式,我个人感觉是更灵活些,也利于对 Hystrix 做统一管理,更加模块化也更收敛。最重要的是,对于老项目接入 Hystrix 的项目来说,改动量相对要小一些。只要设计出适合业务自身的降级逻辑就可以了,在 AOP 中统一配置实现。

7 断路器

Hystrix 本身就提供熔断功能。当服务降级达到一个阀值时,断路器会自动开启,断路器开启后,所有的服务会被拦在外面。


断路器开关由关闭到打开的状态转换是通过当前服务健康状况和设定阈值决定的。


下图 8 显示了断路器开关的流程。


服务的健康状况=请求失败数/请求总数



图 8 断路器


每个断路器默认维护 10 个 bucket,每秒一个 bucket,每个 bucket 记录成功、失败、超时、拒绝的状态,默认为错误超过 50%且 10 秒内超过 20 个请求进行中断拦截。


这里特别要说明一下 10 秒内超过 20 个请求,它的意思是说在统计窗口 10 秒内有至少有 20 个请求并且失败率为 50%,即一个失败就发生熔断。但是, 如果在 10 秒内不足 20 个请求,即使失败率为 100%,也是不满足断路器开启条件的。


可以通过 hystrix.command.default.circuitBreaker.requestVolumeThreshold 配置修改窗口内的失败次数。

8 总结

伴随着分布式应用和服务化以及微服务的开发方式已成为了主流,所以服务间的调用越来越复杂,也越来越频繁。那么如何构建一个高可用和高并发的服务,就变成了我们最为棘手的问题。Hystrix 可以有效的隔离外部坏的服务来保护自己。在流量高峰超过系统处理能力的时候,可以进行快速失败,保证已接收处理的请求不受影响。此外,Hystrix 也被纳入了 spring-cloud 体系,足可以看出其在微服务中的重要性。


作者介绍:


王大可(企业代号名),贝壳找房后端研发工程师。


本文转载自公众号贝壳产品技术(ID:gh_9afeb423f390)。


原文链接:


https://mp.weixin.qq.com/s/4Fg0COnWRB3rRWfxbJt7gA


2019-09-26 16:112038

评论

发布
暂无评论
发现更多内容

百度AI,和“吴文俊奖”同行的十二年、千丈山、万里路

脑极体

百度 吴文俊奖

初学者如何系统性地学习Linux?

海拥(haiyong.site)

三周年连更

Kubernetes数据持久化管理

乌龟哥哥

三周年连更

耗时15天,我把“大厂面试指南”进行了重新梳理,V2.0版已上线

Java你猿哥

Java 数据库 计算机 java面试 java基础

华为云网站安全解决方案助力企业腾“云”驾“务”

YG科技

RabbitMQ - 1消息队列中间件AMQP协议、和主要角色

Java你猿哥

Java ssm AMQP Rabbit MQ

DevData Talks | 思码逸陆春蕊:研发效能度量落地的难点与计策

思码逸研发效能

研发效能

Android App开发超实用实例 | ​Broadcast

TiAmo

broadcast broadcastreceiver Android APP

Spring Data JPA:轻松实现数据持久化

Java你猿哥

Java spring ssm spring data

浪潮信息 KOS 助力企业核心业务完成 CentOS 迁移替换,性能提升 10%|龙蜥案例

OpenAnolis小助手

操作系统 开源社区 CentOS迁移 浪潮信息 龙蜥案例

轻量级云原生大数据平台"CloudEon"正式开源

CloudEon开源

大数据 云原生 服务 解决方案 组件

深入探究HDFS:高可靠、高可扩展、高吞吐量的分布式文件系统【上进小菜猪大数据系列】

上进小菜猪

hdfs hdfs shell 上进小菜猪

kafka高性能设计之内存池

Java你猿哥

Java kafka ssm 架构师 内存池

All in AI,现在开始算不算太晚?

Baihai IDP

人工智能 AI 企业号 5 月 PK 榜 人工智能浪潮

华为云网站安全解决方案:让企业上云后无忧开展网站业务

YG科技

另一个CI/CD构建工具

weichenqi

DevOps 云原生 运维平台

Linux中最低调、最易让人忽视的tmp目录,原来用处那么大!

wljslmz

Linux 三周年连更

全网好评!程序员面试必备的Java八股文,适合所有的Java求职者!

Java你猿哥

Java Spring Boot 多线程 java基础 Java八股文

GitHub最新爆火,2023年最新版互联网大厂Java八股文合集PDF版出炉

开心学Java

Java 面试 Java八股文

华为云网站安全解决方案一站式护航

YG科技

京东APP百亿级商品与车关系数据检索实践 | 京东云技术团队

京东科技开发者

数据库 京东云 企业号 5 月 PK 榜

Flink数据流介绍

阿泽🧸

flink 三周年连更

Mac 配置ChatGLM-6B环境

IT蜗壳-Tango

三周年连更

试用「ChatGPT」几周之后

知了一笑

人工智能 ChatGPT

为什么老有人想让我们“程序员”失业? | 社区征文

坚果

三周年征文

守护企业网站安全!选择华为云网站安全方案更准

YG科技

Spring Boot:MyBatis分页

Java你猿哥

Java spring Spring Boot mybatis ssm

学生管理系统毕设作业

kylexy_0817

架构实战营

应用数据加密技术概述

穿过生命散发芬芳

数据加密 三周年连更

和写作谈谈感觉,你也许可以这样做。

叶小鍵

mosn基于延迟负载均衡算法——走得更快,期待走得更稳 | 京东云技术团队

京东科技开发者

负载均衡 京东云 企业号 5 月 PK 榜

  • 扫码添加小助手
    领取最新资料包
Hystrix在项目中实践_文化 & 方法_王大可_InfoQ精选文章