首页 > 解决方案 > 你如何覆盖 OpenFeign 的 Hystrix 配置?

问题描述

你如何覆盖 OpenFeign 的 Hystrix 默认配置?大多数文档都是针对 SpringBoot + OpenFeign 的,它有自己的 Spring 特定的配置覆盖系统。

理想情况下,可以为客户端配置 Hystrix 核心大小,并基于每个端点配置和超时。

标签: configurationhystrixopenfeign

解决方案


Hystrix OpenFeignsetterFactory()在构建器上有一个方法,允许您传入一个 SetterFactory lambda 函数,该函数在设置每个目标端点时执行:

final SetterFactory hystrixConfigurationFactory = (target, method) -> {
    final String groupKey = target.name();
    final String commandKey = method.getAnnotation(RequestLine.class).value();

    // Configure default thread pool properties
    final HystrixThreadPoolProperties.Setter hystrixThreadPoolProperties = HystrixThreadPoolProperties.Setter()
        .withCoreSize(50)
        .withMaximumSize(200)
        .withAllowMaximumSizeToDivergeFromCoreSize(true);

    return HystrixCommand.Setter
        .withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey))
        .andCommandKey(HystrixCommandKey.Factory.asKey(commandKey))
        .andThreadPoolPropertiesDefaults(hystrixThreadPoolProperties);;
};

final MyTargetClient myTargetClient = HystrixFeign.builder()
    .setterFactory(hystrixConfigurationFactory)
    .client(new OkHttpClient())
    .encoder(new JacksonEncoder(objectMapper))
    .decoder(new JacksonDecoder(objectMapper))
    .target(new Target.HardCodedTarget<>(MyTargetClient.class, "customclientname", baseUrl))

上面的示例使用OpenFeign 文档中的样板来根据目标端点函数正确命名 Hystrix 键。然后,它还进一步将默认线程池属性核心大小和最大核心大小配置为所有目标函数的默认值。

然而,由于这个工厂是为每个目标端点调用的,我们实际上可以在每个端点的基础上覆盖 Hystrix 配置。一个很好的用例是 Hystrix 超时:有时有些端点比其他端点花费更长的时间,我们需要考虑到这一点。

最简单的方法是首先创建一个注释并将其放置在需要覆盖的目标端点上:

/**
 * Override Hystrix configuration for Feign targets.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface HystrixOverride {
    int DEFAULT_EXECUTION_TIMEOUT = 2_000;

    /**
     * Execution timeout in milliseconds.
     */
    int executionTimeout() default DEFAULT_EXECUTION_TIMEOUT;
}
interface MyTargetClient {
    @HystrixOverride(executionTimeout = 10_000)
    @RequestLine("GET /rest/{storeCode}/V1/products")
    Products searchProducts(@Param("storeCode") String storeCode, @QueryMap Map<String, Object> queryMap);

    @RequestLine("GET /rest/{storeCode}/V1/products/{sku}")
    Product getProduct(@Param("storeCode") String storeCode, @Param("sku") String sku);
}

在上面的示例中,搜索 API 可能需要更长的时间来加载,因此我们对此进行了覆盖。

仅仅将覆盖注释放在目标端点函数上是不够的。我们需要回到我们的工厂并更新它以使用注释中的数据:

final SetterFactory hystrixConfigurationFactory = (target, method) -> {
    final String groupKey = target.name();
    final String commandKey = method.getAnnotation(RequestLine.class).value();

    // Configure per-function Hystrix configuration by referencing annotations
    final HystrixCommandProperties.Setter hystrixCommandProperties = HystrixCommandProperties.Setter();
    final HystrixOverride hystrixOverride = method.getAnnotation(HystrixOverride.class);

    final int executionTimeout = (hystrixOverride == null)
        ? HystrixOverride.DEFAULT_EXECUTION_TIMEOUT
        : hystrixOverride.executionTimeout();
    hystrixCommandProperties.withExecutionTimeoutInMilliseconds(executionTimeout);

    // Configure default thread pool properties
    final HystrixThreadPoolProperties.Setter hystrixThreadPoolProperties = HystrixThreadPoolProperties.Setter()
        .withCoreSize(50)
        .withMaximumSize(200)
        .withAllowMaximumSizeToDivergeFromCoreSize(true);

    return HystrixCommand.Setter
        .withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey))
        .andCommandKey(HystrixCommandKey.Factory.asKey(commandKey))
        .andCommandPropertiesDefaults(hystrixCommandProperties)
        .andThreadPoolPropertiesDefaults(hystrixThreadPoolProperties);;
};

以上检查是否存在覆盖注释,然后使用该注释中的数据来配置该目标端点的执行超时。如果不存在覆盖,则将使用 HystrixOverride 端点的默认值。然后将生成的hystrixCommandProperties变量插入到最后的整体HystrixCommand.Setter中。


推荐阅读