Spring cloud Feign + Hystrix 原理(一)

Spring cloud Feign + 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

@EnableHystrix
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class ConsumerApplication {
	public static void main(String[] args) {
		SpringApplication.run(ConsumerApplication.class, args);
	}
}
@Component
public 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)
@Documented
@Inherited
@EnableCircuitBreaker
public @interface EnableHystrix {
}

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

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@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 {

		@Bean
		@ConditionalOnMissingBean
		public Targeter feignTargeter() {
			return new HystrixTargeter();
		}
}

@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
protected static class DefaultFeignTargeterConfiguration {

		@Bean
		@ConditionalOnMissingBean
		public 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 {

		@Bean
		@Scope("prototype")
		@ConditionalOnMissingBean
		@ConditionalOnProperty(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方式进行单步调试分析

你可能感兴趣的:(spring技术分享,spring,boot,spring)