Feign 與 Hystrix


Feign 可以支援 Hystrix,以〈gossip 服務(三)發現〉中的成果為例,若想為 gossipMessageService 加上替代的應變方案,可以如下設置:

package cc.openhome.model;

...略
import cc.openhome.hystrix.MessageServiceFallback;

@FeignClient(value = "msgsvi", fallback = MessageServiceFallback.class)
public interface MessageService {
    ...略

    @GetMapping("newestMessages?n={n}")
    Resources<Message> newestMessages(@PathVariable("n") int n);
}

Hystrix 會包裹全部的方法,MessageServiceFallback 提供了執行替代方案時各自呼叫的方法,必須實現 MessageService,並且必須是個可被 Spring 發現的 Bean,例如:

package cc.openhome.hystrix;

...略

@Service
public class MessageServiceFallback implements MessageService {

    ...略

    @Override
    public Resources<Message> newestMessages(int n) {
        return new Resources<>(Arrays.asList(new Message("gossiper", 0L, "啊嗚!發生問題,自 epoch 之後都沒有八卦!… XD")));
    }

}

記得掃描 Bean 時必須包含這個套件:

@SpringBootApplication(
    scanBasePackages={
        "cc.openhome.controller",
        "cc.openhome.model",
        "cc.openhome.aspect",
        "cc.openhome.hystrix"
    }
)
@EnableFeignClients(
    basePackages={
        "cc.openhome.model"
    }
)
@PropertySource("classpath:path.properties")
public class GossipApplication {

預設對 Hystrix 的支援是關閉的,必須使用 feign.hystrix.enabled 來啟用,因此可以在 application.properties 中加入:

feign.hystrix.enabled=true

如果想為個別的方法設置相關屬性,可以改為 application.yml,並以 yml 語法來設置屬性:

spring:
    thymeleaf:
        cache: false
feign:
    hystrix:
        enabled: true

hystrix:
    command:
        "MessageService#newestMessages(int)":
            execution:
                isolation:
                    thread:
                        timeoutInMilliseconds: 2000

相關的屬性可以在〈Hystrix Configuration〉找到,其中會說明哪些名稱要替換掉,例如,hystrix.command.HystrixCommandKey.execution.isolation.thread.timeoutInMilliseconds 中的 HystrixCommandKey 就是要替換的部份,因為會涉及 dot 符號等問題而使用 yml 來設置。

如果想取消為 @FeignClient 標註的類別中每個方法做包裹的行為,或者是想在 JavaConfig 中自訂相關 Hystrix 屬性,根據 Feign Hystrix Support,可以自訂 Feign.Builder 來達到目的,詳情可查看 feign/hystrix 的說明,一個例子會像是:

@Bean
public Feign.Builder feignHystrixBuilder() {
    return HystrixFeign.builder().setterFactory((target, method) -> {
            return HystrixCommand.Setter
                    // 包裹 MessageService 全部方法
                    .withGroupKey(HystrixCommandGroupKey.Factory.asKey(MessageService.class.getSimpleName()))
                    // 逾時設置
                    .andCommandPropertiesDefaults(
                            HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(1) 
                    );
        }
    );
}