Netflix Hystrix已经停止更新目前处于维护状态,在SpringCloud中对服务的调用通常是用feign完成的,如果同时想使用hystrix做熔断则只需如下配置就可以完成(前提已经引入了feign相关依赖)
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的集成上看看其原理是怎么样的分析思路按照如下顺序进行
首先看下该注解定义发现该注解中开启了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()]);
}
}
}
该类中生成了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();
}
}
此类主要是根据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依赖基本加载完毕
最终在获取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方式进行单步调试分析