天天看点

hystrix原理_Spring cloud Feign + Hystrix 原理(一)

hystrix原理_Spring cloud Feign + Hystrix 原理(一)

hystrix 流程图

Netflix Hystrix已经停止更新目前处于维护状态,在SpringCloud中对服务的调用通常是用feign完成的,如果同时想使用hystrix做熔断则只需如下配置就可以完成(前提已经引入了feign相关依赖)

  • 引入springcloud hystrix starter 依赖
  • 在application.properties 里添加feignclient enable hystrix 配置
  • 在启动主类上增加 @EnableHystrix 注解
  • 在feign接口上配置好fallback类即可
org.springframework.cloud       spring-cloud-starter-netflix-hystrix       
           
feign.hystrix.enbaled=true
@[email protected]@[email protected] class ConsumerApplication {public static void main(String[] args) {SpringApplication.run(ConsumerApplication.class, args);}}
           
@Componentpublic class UserFallBack implements UserFeignClient{    @Override    public String getUser() {        return "fall back";    }}@FeignClient(value = "eureka-provider",fallback = UserFallBack.class)public interface UserFeignClient {    @GetMapping("/user")    String getUser();    }
           

通过以上配置就可以使用带有hystrix的FeignClient了,本文将重点放在Feign与Hystrix的集成上看看其原理是怎么样的分析思路按照如下顺序进行

  • EnableHystrix 工作原理
  • FeignAutoConfiguration 中 HystrixTargeter分析
  • FeignClientsConfiguration中Feign.Builder分析
  • FeignClientFactoryBean中loadBalance分析

EnableHystrix 分析

首先看下该注解定义发现该注解中开启了EnableCircuitBreaker(断路器注解)该注解才是将Hystrix相关beanDefinition扫描至spring容器的关键

@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@[email protected]@EnableCircuitBreakerpublic @interface EnableHystrix {}
           

在EnableCircuitBreaker接口中出现了熟悉的Import注解,此注解中EnableCircuitBreakerImportSelector类将完成bean的扫描工作

@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@[email protected]@Import({EnableCircuitBreakerImportSelector.class})public @interface EnableCircuitBreaker {}
           

接下来分析下EnableCircuitBreakerImportSelector工作原理,首先进入EnableCircuitBreakerImportSelector发现只有一个isEnable方法而且默认是开启状态,此时selectImports方法并没有Override父类中的方法直接查看父类中该方法

@Order(2147483547)public class EnableCircuitBreakerImportSelector extends SpringFactoryImportSelector {    public EnableCircuitBreakerImportSelector() {    }    protected boolean isEnabled() {        return (Boolean)this.getEnvironment().getProperty("spring.cloud.circuit.breaker.enabled", Boolean.class, Boolean.TRUE);    }}
           

由于enable默认为true,执行else中方法最为重要的方法为SpringFactoriesLoader.loadFactoryNames,如果看过springboot启动主类的源码话该方法的作用就是将jar包中META-INF/spring.factories解析并加入到容器中而HystrixCircuitBreakerConfiguration为hystrix核心配置类会初始化相关bean,可查阅相关类进行查看

LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(this.annotationClass, this.beanClassLoader)));
public String[] selectImports(AnnotationMetadata metadata) {        if (!this.isEnabled()) {            return new String[0];        } else {            AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(this.annotationClass.getName(), true));            Assert.notNull(attributes, "No " + this.getSimpleName() + " attributes found. Is " + metadata.getClassName() + " annotated with @" + this.getSimpleName() + "?");            List factories = new ArrayList(new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(this.annotationClass, this.beanClassLoader)));            if (factories.isEmpty() && !this.hasDefaultFactory()) {                throw new IllegalStateException("Annotation @" + this.getSimpleName() + " found, but there are no implementations. Did you forget to include a starter?");            } else {                if (factories.size() > 1) {                    this.log.warn("More than one implementation of @" + this.getSimpleName() + " (now relying on @Conditionals to pick one): " + factories);                }                return (String[])factories.toArray(new String[factories.size()]);            }        }    }
           

FeignAutoConfiguration

该类中生成了Targeter而Targeter的生成是根据classpath下是否存在 feign.hystrix.HystrixFeign内存则生成HystrixTargeter

@Configuration(proxyBeanMethods = false)@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")protected static class HystrixFeignTargeterConfiguration {@[email protected] Targeter feignTargeter() {return new HystrixTargeter();}}@Configuration(proxyBeanMethods = false)@ConditionalOnMissingClass("feign.hystrix.HystrixFeign")protected static class DefaultFeignTargeterConfiguration {@[email protected] Targeter feignTargeter() {return new DefaultTargeter();       }}
           

FeignClientsConfiguration

此类主要是根据applicaton.yml或application.properties是否存在如下配置

feign.hystrix.enabled = true

如果classpath同时 存在HystrixCommand.class, HystrixFeign.class 则Feign.Builder 为HystrixFeign.builder()

@Configuration(proxyBeanMethods = false)@ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })protected static class HystrixFeignConfiguration {@[email protected]("prototype")@[email protected](name = "feign.hystrix.enabled")public Feign.Builder feignHystrixBuilder() {return HystrixFeign.builder();}}
           

至此Hystrix依赖基本加载完毕

FeignClientFactoryBean

最终在获取FeignClient对象的时候通过FeignClientFactoryBean类getObject方法,最终会调用getTarget方法,此方法中会寻找Feign.Builder 也就是Hystix builder至此FeignClient与Hystrix整合完成

T getTarget() {FeignContext context = this.applicationContext.getBean(FeignContext.class);Feign.Builder builder = feign(context);if (!StringUtils.hasText(this.url)) {if (!this.name.startsWith("http")) {this.url = "http://" + this.name;}else {this.url = this.name;}this.url += cleanPath();return (T) loadBalance(builder, context,new HardCodedTarget<>(this.type, this.name, this.url));}.........省略部分代码.................}
           

总结

其实整合流程并不复杂,在接下来的文章会继续分析Hystrix的工作原理,如果对整合流程的细节比较感兴趣可以通过debug方式进行单步调试分析

下一篇文章主要对Hystrix进行分析