基于springcloud的灰度实现方案(三)-feign调用

基于springcloud的灰度实现方案(一)

基于springcloud的灰度实现方案(二)

前两篇介绍了灰度方案以及灰度的实现,这篇从feign底层调用上分析一下是如何实现的。

首先,我们在feign调用时,使用了FeignClient注解。

#接口调用
@FeignClient("demo-service")
public interface DemoServiceFeginClient {
}
# 开启feign
@EnableFeignClients(basePackages = {"com.yxkong.api"})
public class ApiStarter {
}

定位下发现注解位于

FeignClient和EnableFeignClients  位于
Maven: 
    org.springframework.cloud
    spring-cloud-openfeign-core
    2.2.8.RELEASE

我们看下EnableFeignClients的源码

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
//关键点,利用spring的动态注册bean的机制,
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {

class FeignClientsRegistrar
    implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
  @Override
  public void registerBeanDefinitions(AnnotationMetadata metadata,
      BeanDefinitionRegistry registry)
     //注册feign配置到registry
    registerDefaultConfiguration(metadata, registry);
    //根据配置扫描的feignClient,生成bean并注册到registry    
    registerFeignClients(metadata, registry);
  }    
}
  public void registerFeignClients(AnnotationMetadata metadata,
      BeanDefinitionRegistry registry) {
    //关键代码,遍历所有的client
    for (String basePackage : basePackages) {
        candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
    }
  }

整个feign的逻辑流程如下:

基于springcloud的灰度实现方案(三)-feign调用_第1张图片

这里又有两个点:

1,Client 用哪个?怎么注入的?

2,Targeter用哪个?

回过头来,继续看

   T getTarget() {
    //context 从哪里来?
    FeignContext context = beanFactory != null
        ? beanFactory.getBean(FeignContext.class)
        : applicationContext.getBean(FeignContext.class);
    //Feign又从哪来?
    Feign.Builder builder = feign(context);
  }

基于springcloud的灰度实现方案(三)-feign调用_第2张图片

其中

Defult是feign的默认实现,内部使用HttpURLConnection 执行调用;

LoadBalancerFeignClient是在org.springframework.cloud.openfeign.ribbon包下;

剩下的两个在org.springframework.cloud.openfeign.loadbalancer 包下。

这种注入性的配置在springboot里一般都在configuration中。

看下spring.factorieswe文件

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration,\
org.springframework.cloud.openfeign.hateoas.FeignHalAutoConfiguration,\
org.springframework.cloud.openfeign.FeignAutoConfiguration,\
org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration,\
org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration,\
org.springframework.cloud.openfeign.loadbalancer.FeignLoadBalancerAutoConfiguration

AutoConfiguration 是有顺序的。

在FeignRibbonClientAutoConfiguration中,

//第一个就是HttpClientFeignLoadBalancedConfiguration
@Import({ HttpClientFeignLoadBalancedConfiguration.class,
    OkHttpFeignLoadBalancedConfiguration.class,
    HttpClient5FeignLoadBalancedConfiguration.class,
    DefaultFeignLoadBalancedConfiguration.class })
public class FeignRibbonClientAutoConfiguration {
}
@Configuration(proxyBeanMethods = false)
//没引入这个类,不执行
@ConditionalOnClass(ApacheHttpClient.class)
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
@Conditional(HttpClient5DisabledConditions.class)
//这里又导入了HttpClient
@Import(HttpClientFeignConfiguration.class)
class HttpClientFeignLoadBalancedConfiguration {
}
@Configuration(proxyBeanMethods = false)
//这个也没引入,不执行
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnProperty("feign.okhttp.enabled")
@Import(OkHttpFeignConfiguration.class)
class OkHttpFeignLoadBalancedConfiguration {
}
@Configuration(proxyBeanMethods = false)
//这个没引入,不执行
@ConditionalOnClass(ApacheHttp5Client.class)
@ConditionalOnProperty(value = "feign.httpclient.hc5.enabled", havingValue = "true")
@Import(HttpClient5FeignConfiguration.class)
class HttpClient5FeignLoadBalancedConfiguration {
}

再看最后一个

@Configuration(proxyBeanMethods = false)
class DefaultFeignLoadBalancedConfiguration {

  @Bean
  @ConditionalOnMissingBean
  public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
      SpringClientFactory clientFactory) {
    return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory,
        clientFactory);
  }
}

到这,默认的Client 的注入有了。

我们再看

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({ FeignClientProperties.class,
    FeignHttpClientProperties.class, FeignEncoderProperties.class })
@Import(DefaultGzipDecoderConfiguration.class)
public class FeignAutoConfiguration {
  @Configuration(proxyBeanMethods = false)
  @Conditional(DefaultFeignTargeterConditions.class)
  protected static class DefaultFeignTargeterConfiguration {

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

  }

  @Configuration(proxyBeanMethods = false)
  @Conditional(FeignCircuitBreakerDisabledConditions.class)
  @ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
  //优先使用这个
  @ConditionalOnProperty(value = "feign.hystrix.enabled", havingValue = "true",
      matchIfMissing = true)
  protected static class HystrixFeignTargeterConfiguration {

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

  }

  @Configuration(proxyBeanMethods = false)
  @ConditionalOnClass(CircuitBreaker.class)
  @ConditionalOnProperty(value = "feign.circuitbreaker.enabled", havingValue = "true")
  protected static class CircuitBreakerPresentFeignTargeterConfiguration {

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnBean(CircuitBreakerFactory.class)
    public Targeter circuitBreakerFeignTargeter(
        CircuitBreakerFactory circuitBreakerFactory) {
      return new FeignCircuitBreakerTargeter(circuitBreakerFactory);
    }

  }
}

到这,默认的Targeter 是 HystrixTargeter。

到此feign初始化的流程分析完了。

下篇接着分析路由。

如果觉得对你有帮助,请关注公众号:5ycode,后续会不断更新哦

公众号图片

你可能感兴趣的:(架构,灰度,分布式,spring,boot,feign,java)