Как переопределить конфигурацию Hystrix для OpenFeign?

#configuration #hystrix #openfeign

Вопрос:

Как переопределить конфигурацию Hystrix по умолчанию для OpenFeign? Большая часть документации предназначена для SpringBoot OpenFeign, которая имеет свою собственную систему переопределения конфигурации, специфичную для Spring.

В идеале можно было бы настроить размер ядра Hystrix для клиента, а также настроить и время ожидания для каждой конечной точки.

Ответ №1:

В Hystrix OpenFeign есть setterFactory() метод в построителе, который позволяет передавать лямбда-функцию SetterFactory, которая выполняется при настройке каждой целевой конечной точки:

 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 в конце.