spring cloud 和 netflix hystrix 的客户端弹性模式

hystrix

  1. 弹性模式:
    1. 客户端负载均衡模式(ribbon);
    2. 断路器模式(通过快速失败,阻止对远程资源的调用);
    3. 后备模式(提供后备方法);
    4. 舱壁模式(在服务中通常会调用多个服务的资源,默认情况下hystrix命令共享同一个线程池(10个线程)来处理请求,给每个服务的调用分配一个线程池,当一个服务响应慢时不会拖累对其他服务的调用);
  2. 搭建微服务以使用hystrix;
    1. 导入依赖包;
      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-hystrix</artifactId>
      </dependency>
    2. 在引导类添加@EnabkeCircuitBreaker注解修饰,告诉spring开启hystrix;

    3. 使用@HystrixCommand注解对方法进行标记(举例如下);

      package com.thoughtmechanix.licenses.services;
      
      import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
      import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
      import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
      
      import com.thoughtmechanix.licenses.repository.LicenseRepository;
      
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Service;
      
      import java.util.ArrayList;
      import java.util.List;
      
      @Service
      @DefaultProperties(
              threadPoolKey = "licenseByOrgThreadPool",
              threadPoolProperties =
                      {@HystrixProperty(name = "coreSize", value = "30"),
                              @HystrixProperty(name = "maxQueueSize", value = "10")},
              commandProperties = {
                      @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "12000"),
                      @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
                      @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "75"),
                      @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "7000"),
                      @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "15000"),
                      @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "5")}
      )
      public class LicenseService {
          private static final Logger logger = LoggerFactory.getLogger(LicenseService.class);
          @Autowired
          private LicenseRepository licenseRepository;
      
          @HystrixCommand(fallbackMethod = "buildFallbackLicenseList",
                  threadPoolKey = "licenseByOrgThreadPool",
                  threadPoolProperties =
                          {@HystrixProperty(name = "coreSize", value = "30"),
                                  @HystrixProperty(name = "maxQueueSize", value = "10")},
                  commandProperties = {
                          @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "12000"),
                          @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
                          @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "75"),
                          @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "7000"),
                          @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "15000"),
                          @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "5")}
          )
          public List<License> getLicensesByOrg(String organizationId) {
              logger.debug("LicenseService.getLicensesByOrg  Correlation id: {}", UserContextHolder.getContext().getCorrelationId());
              randomlyRunLong();
      
              return licenseRepository.findByOrganizationId(organizationId);
          }
      
          private List<License> buildFallbackLicenseList(String organizationId) {
              List<License> fallbackList = new ArrayList<>();
              License license = new License()
                      .withId("0000000-00-00000")
                      .withOrganizationId(organizationId)
                      .withProductName("Sorry no licensing information currently available");
      
              fallbackList.add(license);
              return fallbackList;
          }
      
      }
      

      注意:后备方法必须与@HystrixCommand保护的原始方法位于同一个类中,且具有与原始方法完全相同的方法签名。

    4. hystrix注解中配置说明:

      属性名称默认值描述

      execution.isolation.t

      hread.timeoutInMilliseconds

      1000设置断路器超时时间
      fallbackMethodNone远程调用超时时间调用该方法,后备方法必须与原始方法位于同一个类中,且具有与原始方法完全相同的方法签名
      threadPoolKeyNone创建一个独立与默认线程池的线程池,如果没有定义任何值,则使用默认的hystrix线程池
      threadPoolPropertiesNone配置线程池的属性
      coreSize10设置线程池大小
      maxQueueSize-1设置请求排队的队列,-1表示使用SynchronousQueue来保存请求,本质上会强制要求正在处理的请求数量不能超过线程池中的可用线程数量;大于1时使用LinkedBlockingQueue来保存请求

      circuitBreaker.

      requestVolumeThreshold

      20设置hystrix开始检查断路器是否跳闸之前滚动窗口中必须处理的最小请求数量

      circuitBreaker.

      errorThresholdPercentage

      50在断路器跳闸之前,滚动窗口内必须达到的故障百分比

      circuitBreaker.

      sleepWindowInMilliseconds

      5000在断路器跳闸之后,hystrix尝试进行远程服务调用之前要等待的时间

      metrics.rollingStats.

      timeInMilliseconds

      10000hystrix收集和监控服务调用的统计信息给的滚动窗帘

      metrics.rollingStats.

      numBuckets

      10hystrix在一个监控窗口中维护的度量桶的数量
      设置类级别的hystrix,使用@DefaultProperties,看上述代码
    5. 线程上下文和hystrix;

      1. 使用@HystrixCommand可以设置两种不同的隔离策略:

        1. thread(线程)级别:在单独的线程中运行,不与父线程共享它的上下文,同时hystrix也不会将父线程的上下文传播到hystrix命令,此时无法使用父线程上下文中的内容(如父线程中设置在ThreadLocal中的值);

        2. semaphone(信号量)级别:不启动新线程,在当前线程中运行,不建议使用,如果调用超时,会中断父线程并抛出异常;

      2. hystrix提供了一种机制,可以将父线程的上下文传播到hystrix命令线程池管理的线程,改机制为HystrixConcurrencyStrategy,实现过程如下:

        1. 自定义hystrix并发策略(默认情况下hystrix自允许为应用程序定义一个HystrixConcurrencyStrategy,springcloud已经定义了一个并发策略用于处理spring安全信息的传播,springcloud允许将自定义的并发策略与之链接在一起);

          package com.thoughtmechanix.organization.hystrix;
          
          import com.netflix.hystrix.HystrixThreadPoolKey;
          import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
          import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariable;
          import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariableLifecycle;
          import com.netflix.hystrix.strategy.properties.HystrixProperty;
          import com.thoughtmechanix.organization.utils.UserContextHolder;
          
          import java.util.concurrent.BlockingQueue;
          import java.util.concurrent.Callable;
          import java.util.concurrent.ThreadPoolExecutor;
          import java.util.concurrent.TimeUnit;
          
          /**
           * 注意:所有可能被覆盖的方法都需要检查现有的并发策略是否存在,
           * 存在则调用现有的并发策略的方法,不存在则调用基类并发策略中的方法,
           * 否则,在受hystrix保护的代码中使用spring安全上下文时,可能会遇到难以解决的问题
           */
          public class ThreadLocalAwareStrategy extends HystrixConcurrencyStrategy{
              private HystrixConcurrencyStrategy existingConcurrencyStrategy;
          
              /**
               * spring cloud 已经定义了并发策略,将其传入到自定义的并发策略中进行链接
               * @param existingConcurrencyStrategy
               */
              public ThreadLocalAwareStrategy(
                      HystrixConcurrencyStrategy existingConcurrencyStrategy) {
                  this.existingConcurrencyStrategy = existingConcurrencyStrategy;
              }
          
              @Override
              public BlockingQueue<Runnable> getBlockingQueue(int maxQueueSize) {
                  return existingConcurrencyStrategy != null
                          ? existingConcurrencyStrategy.getBlockingQueue(maxQueueSize)
                          : super.getBlockingQueue(maxQueueSize);
              }
          
              @Override
              public <T> HystrixRequestVariable<T> getRequestVariable(
                      HystrixRequestVariableLifecycle<T> rv) {
                  return existingConcurrencyStrategy != null
                          ? existingConcurrencyStrategy.getRequestVariable(rv)
                          : super.getRequestVariable(rv);
              }
          
              @Override
              public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
                                                      HystrixProperty<Integer> corePoolSize,
                                                      HystrixProperty<Integer> maximumPoolSize,
                                                      HystrixProperty<Integer> keepAliveTime, TimeUnit unit,
                                                      BlockingQueue<Runnable> workQueue) {
                  return existingConcurrencyStrategy != null
                          ? existingConcurrencyStrategy.getThreadPool(threadPoolKey, corePoolSize,
                          maximumPoolSize, keepAliveTime, unit, workQueue)
                          : super.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize,
                          keepAliveTime, unit, workQueue);
              }
          
              /**
               * 将自定义Callable注入进来,DelegatingUserContextCallable是自定义的Callable,
               * 构造器中将父线程的callable和需要传递的上下文信息传入给自定义Callable
               * @param callable
               * @param <T>
               * @return
               */
              @Override
              public <T> Callable<T> wrapCallable(Callable<T> callable) {
                  return existingConcurrencyStrategy != null
                          ? existingConcurrencyStrategy
                          .wrapCallable(new DelegatingUserContextCallable<T>(callable, UserContextHolder.getContext()))
                          : super.wrapCallable(new DelegatingUserContextCallable<T>(callable, UserContextHolder.getContext()));
              }
          }
          

          UserContextHolder类将信息存储在ThreadLocal:

          package com.thoughtmechanix.organization.utils;
          
          
          import org.springframework.util.Assert;
          import java.util.HashMap;
          import java.util.Map;
          
          public class UserContextHolder {
              private static final ThreadLocal<Map<String, Object>> userContext = new ThreadLocal<>();
          
              public static final  Map<String, Object> getContext() {
                  Map<String, Object> context = userContext.get();
                  if (context == null) {
                      context = new HashMap<>();
                      userContext.set(context);
                  }
                  return userContext.get();
              }
          
              public static final void setContext( Map<String, Object> context) {
                  Assert.notNull(context, "Only non-null UserContext instances are permitted");
                  userContext.set(context);
              }
          
          }
          
        2. 定义Callable类,将父线程的上下文内容注入到hystrix线程中;

          package com.thoughtmechanix.organization.hystrix;
          
          import com.thoughtmechanix.organization.utils.UserContextHolder;
          import org.springframework.util.Assert;
          
          import java.util.Map;
          import java.util.concurrent.Callable;
          
          
          public final class DelegatingUserContextCallable<V> implements Callable<V> {
              private final Callable<V> delegate;
          
              private  Map<String,Object>  originalUserContext;
          
              /**
               * 原始的父线程Callable类和上下文信息被传递到hystrix线程中要使用的自定义Callable中
               * @param delegate
               * @param userContext
               */
              public DelegatingUserContextCallable(Callable<V> delegate,
                                                       Map<String,Object> userContext) {
                  Assert.notNull(delegate, "delegate cannot be null");
                  Assert.notNull(userContext, "userContext cannot be null");
                  this.delegate = delegate;
                  this.originalUserContext = userContext;
              }
          
              /**
               * call方法在@HstrixCommand修饰的方法调用之前调用
               * @return
               * @throws Exception
               */
              public V call() throws Exception {
                  //将父线程传递过来的上下文内容保存到hystrix线程上下文中
                  UserContextHolder.setContext( originalUserContext );
                  try {
                      //此处会调用@HstrixCommand修饰的方法
                      return delegate.call();
                  }
                  finally {
                      this.originalUserContext = null;
                  }
              }
          
              public String toString() {
                  return delegate.toString();
              }
          
          }
        3. 配置springcloud使用自定义的hystrix并发策略;

          package com.thoughtmechanix.organization.hystrix;
          
          import com.netflix.hystrix.strategy.HystrixPlugins;
          import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
          import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier;
          import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook;
          import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher;
          import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy;
          import org.springframework.beans.factory.annotation.Autowired;
          import org.springframework.context.annotation.Configuration;
          
          import javax.annotation.PostConstruct;
          
          @Configuration
          public class ThreadLocalConfiguration {
              //当构造配置对象时,spring会尝试将它自动装配在现有的HystrixConcurrencyStrategy中
              @Autowired(required = false)
              private HystrixConcurrencyStrategy existingConcurrencyStrategy;
          
              @PostConstruct
              public void init() {
                  // 保留现有的Hystrix插件的引用,在注册完新的并发策略后,需要将这些插件重新注册进来
                  HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance()
                          .getEventNotifier();
                  HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance()
                          .getMetricsPublisher();
                  HystrixPropertiesStrategy propertiesStrategy = HystrixPlugins.getInstance()
                          .getPropertiesStrategy();
                  HystrixCommandExecutionHook commandExecutionHook = HystrixPlugins.getInstance()
                          .getCommandExecutionHook();
                  //重置 Hystrix插件
                  HystrixPlugins.reset();
          
                  // 将自定义并发策略注册到插件中,并将之前的插件注册进来
                  HystrixPlugins.getInstance().registerConcurrencyStrategy(new ThreadLocalAwareStrategy(existingConcurrencyStrategy));
                  HystrixPlugins.getInstance().registerEventNotifier(eventNotifier);
                  HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher);
                  HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy);
                  HystrixPlugins.getInstance().registerCommandExecutionHook(commandExecutionHook);
              }
          }
          

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值