【问题标题】:Programmatically changing Hystrix properties以编程方式更改 Hystrix 属性
【发布时间】:2017-02-27 19:03:35
【问题描述】:

我设置了一个断路器,我想更改运行时的参数。线程和超时之类的东西需要在客户现场进行调整。

我像这样创建一个 HystrixCommandProperties.Setter:

HystrixCommandProperties.Setter hystrixProps = 
    HystrixCommandProperties.defaultSetter()
        .withCircuitBreakerSleepWindowInMilliseconds(myconf.sleepWindow);
HystrixThreadPoolProperties.Setter threadPoolSettings = 
    HystrixThreadPoolProperties.Setter()
        .withCoreSize(myconf.threadPoolSize);

new MyCommand(HystrixCommand.Setter.withGroupKey("mygroup")
    .andCommandPropertiesDefaults(hystrixProps)
    .andThreadPoolPropertiesDefaults(threadPoolSettings));

MyCommand 实现标准 HystrixCommand 并调用 super(hystrixProps)。

这是第一次工作,但是当我尝试在运行时更改属性(相同的组名)时没有任何反应。还有另一种以编程方式改变这种情况的方法吗?

我不想浏览属性文件或指定 Archaius 的 URL。

还有一些答案告诉我使用 ConfigurationManager.getConfigInstance().setProperty("...") 通过 Archaius。但是肯定有一种类似于我创建的原始设置器的方法吗?这样做完全不同,因为这是第二次,感觉很尴尬。

【问题讨论】:

    标签: java hystrix circuit-breaker


    【解决方案1】:

    对我来说 HystrixPropertiesStrategyConfigurationManager.getConfigInstance().setProperty(..) 没有帮助,我最终创建了自己的 HystrixDynamicProperty 实现,如下所示,

    public CustomHystrixDynamicProperty extends HystrixDynamicPropertiesArchaius {
    
        @Override
        public HystrixDynamicProperty<Integer> getInteger(final String name, final Integer fallback) {
             if(name.equals("Your hystrix Property")){
                return new HystrixDynamicProperty<Integer>() {
                public Integer get() {
                   // Place your logic here...
                  return yourValue;
                 }
               public String getName(){ return name;};
               public void addCallback(Runnable callback) {};
    
                  }
               } else {
                    return super.getInteger(name,fallback);
                     }
    
    
        }
    }
    

    并在启动 spring boot 应用程序之前添加下面的属性。

    System.setProperty("hystrix.plugin.HystrixDynamicProperties.implementation","YourPackage.CustomHystrixDynamicProperty");
    

    对于好奇的开发人员::HystrixPropertiesChainedProperty 中的 getDynamicProperty 方法正在通过 HystrixPropertiesStrategy 覆盖被覆盖的值,每次在设置 HystrixCommand 之前都会由“AbstractCommand”调用执行, 所以 HystrixPropertiesStrategy 的想法对我不起作用。

    【讨论】:

      【解决方案2】:

      有一种非常简单的方法可以做到这一点。它只需要2个步骤: 一种。注册正确的插件 湾。添加具有所需覆盖的正确策略。

      用例:将 ExecutionTimeoutInMilliseconds 从 1000 毫秒改写为 30000 毫秒

      HystrixPlugins.getInstance().registerPropertiesStrategy(new HystrixPropertiesStrategy() {
                  @Override
                  public HystrixCommandProperties getCommandProperties(HystrixCommandKey commandKey, HystrixCommandProperties.Setter builder) {
                      HystrixCommandProperties.Setter timeout
                              = HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(30000);
                      return super.getCommandProperties(commandKey, timeout);
                  }
              });
      

      这里我只是覆盖了所需的属性。当您运行应用程序时,您可以在 DEBUG 模式下看到:

      2018-06-08 23:18:32 [main] DEBUG c.n.h.s.p.HystrixPropertiesChainedProperty - Flipping property: hystrix.command.Client#getAllData().execution.isolation.thread.timeoutInMilliseconds to use its current value: 30000
      

      【讨论】:

        【解决方案3】:

        迟到的答案,但今天我在同样的事情上挣扎并找到了办法。

        默认属性管理器的实现方式是它根据您运行的命令的名称使用HystrixCommandProperties 的缓存。在第一次使用命令时,它会缓存从传递给命令构造函数的HystrixCommandProperties.Setter 中得到的内容,仅此而已。

        但是,使用自定义 HystrixPropertiesStrategy 作为 Plugin 您可以覆盖缓存键(并因此强制 Hystrix 重新评估传递给新 Command 实例的 Setter,因为缓存键是新的,所以它认为这是一个新命令)。

        然后代码将如下所示:

        public HystrixCommandFactory(....) {
            HystrixPlugins.getInstance().registerPropertiesStrategy(new HystrixPropertiesStrategyWithReloadableCache());
            updateHystrixSettings();        
        }
        
        //configurable attributes
        private volatile int commandTimeoutMillis;
        private volatile long lastSettingsUpdatedTimestamp;
        private volatile HystrixCommand.Setter setterForNewCommands;
        
        private void updateHystrixSettings() {
            lastSettingsUpdatedTimestamp = LocalDateTime.now().toDateTime().getMillis();
            HystrixCommandProperties.Setter propertiesSetter = HystrixCommandProperties.Setter()
                .withExecutionTimeoutInMilliseconds(commandTimeoutMillis)
                .withExecutionTimeoutEnabled(true);
        
            this.setterForNewCommands = HystrixCommand.Setter
                .withGroupKey(HystrixCommandGroupKey.Factory.asKey(GROUP_NAME))
                .andCommandPropertiesDefaults(propertiesSetter);
        
        }
        
        public void setCommandTimeoutMillis(int commandTimeoutMillis) {     
            this.commandTimeoutMillis = commandTimeoutMillis;
            updateHystrixSettings();        
        }
        
        private class HystrixPropertiesStrategyWithReloadableCache extends HystrixPropertiesStrategy {
        
            @Override
            public String getCommandPropertiesCacheKey(HystrixCommandKey commandKey, HystrixCommandProperties.Setter builder) {
                return String.format("%s-%d", commandKey.name(), lastSettingsUpdatedTimestamp);
            }
        }
        

        或者,您总是可以从getCommandPropertiesCacheKey 方法返回null(这会完全关闭缓存),但是Hystrix 必须在每次调用命令时重新构建HystrixCommandProperties 的开销

        PS:一定要使用正确的线程同步来读取和更新这些属性,因为它们会从不同的线程中调用。为了简单起见,我在此示例中省略了这一点,但实际上我在代码中使用了ReentrantReadWriteLock 来保护对这些变量的访问

        【讨论】:

          【解决方案4】:

          供将来参考:我最终通过 ConfigurationManager 和字符串属性使用设置。

          ConfigurationManager.getConfigInstance().setProperty("...")
          

          它让我改变了一些东西,但以一种比原始代码更不安全的方式。我确实为字符串中的拼写错误挣扎了一段时间,这就是为什么我想避免这种情况。

          我现在将它用于我需要更改运行时的所有属性。每次发生变化时创建一个新的 Hystrix 断路器(新的命令键)也是一种选择,但稍后会使用属性文件中断。

          【讨论】:

          • 我面临着类似的问题 - 一旦我在某个组中创建了第一个带有 Setter 的 HystrixCommand,我以后无法通过使用不同的 Setter 创建新的 HystrixCommand 来更改属性(在同一个团体)。那么,hystrix 是否确实以某种方式从它获得的第一个 Setter 中“缓存”了属性,并且除了使用 ConfigurationManager 之外,真的没有办法更改属性吗?
          【解决方案5】:

          Hystrix 属性也可以在我们的服务类里面设置 @HystrixCommand 注解,为此我们使用Hystrix-Javanica 用于在我们的项目中实现注释的项目。 为此,我们需要将 hystrix-javanica 的依赖添加到我们的 类路径。

          对 Maven 的依赖:

          <dependency>
              <groupId>com.netflix.hystrix</groupId>
              <artifactId>hystrix-javanica</artifactId>
              <version>x.y.z</version>
          </dependency>
          

          在@HystrixCommand 注解中,我们可以使用@HystrixProperty 来设置hystrix 的属性。

          @HystrixCommand 属性设置示例:

          @HystrixCommand(groupKey = "StoreSubmission", commandKey = "StoreSubmission", threadPoolKey = "StoreSubmission", commandProperties = {
                  @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "30000"),
                  @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "4"),
                  @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "60000"),
                  @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "180000") }, threadPoolProperties = {
                  @HystrixProperty(name = "coreSize", value = "30"),
                  @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "180000") })
          public String storeSubmission(ReturnType returnType, InputStream is, String id) {
          }
          

          定义这些属性的最佳方式是在外部化的 application.yaml 中,这样您可以更好地控制它并针对不同的环境进行更改。

          这是我的 application.yaml 中的示例 hystrix 配置

          hystrix:
             command.StoreSubmission.execution.isolation.thread.timeoutInMilliseconds: 30000
             command.StoreSubmission.circuitBreaker.requestVolumeThreshold: 4
             command.StoreSubmission.circuitBreaker.sleepWindowInMilliseconds: 60000
             command.StoreSubmission.metrics.rollingStats.timeInMilliseconds: 180000
             collapser.StoreSubmission.maxRequestsInBatch: 1
             collapser.StoreSubmission.requestCache.enabled: FALSE
             threadpool.StoreSubmission.coreSize: 30
             threadpool.StoreSubmission.metrics.rollingStats.timeInMilliseconds: 180000
          

          application.yml 文件的确切格式是

          hystrix:
              command:
                  findAllProducts:
                      execution:
                          isolation:
                              thread:
                                  timeoutInMilliseconds: 1000
                      circuitBreaker:
                          requestVolumeThreshold: 20
                          errorThresholdPercentage: 50
                      metrics:
                          rollingStats:
                              timeInMilliseconds: 10000
                              numBuckets: 10
              threadpool:
                  ProductService:
                      coreSize: 10
          

          有关 Hystrix-Javanica 的更多信息,请访问here

          【讨论】:

          • 我需要在运行时以编程方式更改属性,所以您并没有真正回答我的问题。 Javanica 很好,但我们没有让人们通过文件更改参数。理论上我可以从代码写入文件,但这比我现在正在做的更麻烦和间接的方式: ConfigurationManager.getConfigInstance().setProperty("...") 。我只是想在第一次初始化后使用强类型 API 来更改属性,就像我一开始所做的那样。字符串和可能的拼写错误/误解会妨碍您。就像我切换到它时那样......
          猜你喜欢
          • 2021-09-03
          • 2016-05-22
          • 1970-01-01
          • 2010-12-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-05-06
          • 2014-11-07
          相关资源
          最近更新 更多