ITPub博客

首页 > 数据库 > 数据库开发技术 > feign构建与调用全流程分析

feign构建与调用全流程分析

原创 数据库开发技术 作者:yxkong 时间:2021-09-14 18:37:00 0 删除 编辑

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

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

@Configuration配置加载分析

之前介绍了灰度方案以及实现,分析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的机制,在 Configuration配置加载分析 中已经详细解说@Import(FeignClientsRegistrar.class)public @interface EnableFeignClients {class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware   @Override    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry)     //如果有配置defaultConfiguration 则注册到ioc    registerDefaultConfiguration(metadata, registry);    //根据配置扫描的feignClient,生成bean并注册到registry        registerFeignClients(metadata, registry);  }      public void registerFeignClients(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {
LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();    Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());    //从EnableFeignClients查找配置的clients    final Class<?>[] clients = attrs == null ? null: (Class<?>[]) attrs.get("clients"); if (clients == null || clients.length == 0) { ClassPathScanningCandidateComponentProvider scanner = getScanner(); scanner.setResourceLoader(this.resourceLoader);      //设置过滤条件FeignClient scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));      //在getBasePackages将所有的路径聚合 Set<String> basePackages = getBasePackages(metadata); for (String basePackage : basePackages) {        //将扫描到的所有FeignClient放入candidateComponents candidateComponents.addAll(scanner.findCandidateComponents(basePackage)); }    } for (BeanDefinition candidateComponent : candidateComponents) { if (candidateComponent instanceof AnnotatedBeanDefinition) {     //构建FeignClientFactoryBean注入ioc中 registerFeignClient(registry, annotationMetadata, attributes); } } } private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map<String, Object> attributes) { String className = annotationMetadata.getClassName(); Class clazz = ClassUtils.resolveClassName(className, null); ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory ? (ConfigurableBeanFactory) registry : null; String contextId = getContextId(beanFactory, attributes); String name = getName(attributes);    //真正的是这块,构建一个FeignClientFactoryBean并注入到ioc中 FeignClientFactoryBean factoryBean = new FeignClientFactoryBean(); factoryBean.setBeanFactory(beanFactory);    // 注入对象的名称是配置的或者spring自动生成 factoryBean.setName(name); factoryBean.setContextId(contextId);    //对象类型就是FeignClient标注的对象本身(如此通过@Autowird能快速获取到对应的对象) factoryBean.setType(clazz); BeanDefinitionBuilder definition = BeanDefinitionBuilder .genericBeanDefinition(clazz, () -> { factoryBean.setUrl(getUrl(beanFactory, attributes)); factoryBean.setPath(getPath(beanFactory, attributes)); factoryBean.setDecode404(Boolean .parseBoolean(String.valueOf(attributes.get("decode404")))); Object fallback = attributes.get("fallback"); if (fallback != null) { factoryBean.setFallback(fallback instanceof Class ? (Class<?>) fallback : ClassUtils.resolveClassName(fallback.toString(), null)); } Object fallbackFactory = attributes.get("fallbackFactory"); if (fallbackFactory != null) { factoryBean.setFallbackFactory(fallbackFactory instanceof Class ? (Class<?>) fallbackFactory : ClassUtils.resolveClassName(fallbackFactory.toString(), null)); }          //从FeignClientFactoryBean中获取构建的Feign对象 return factoryBean.getObject();        });
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);    //最终将Feign对象注入到IOC中 BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry); }}

我们看到FeignClientFactoryBean.getObject()中调用了getTarget()方法

public class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,    ApplicationContextAware, BeanFactoryAware {  @Override  public Object getObject() {    return getTarget();  }  <T> T getTarget() {    FeignContext context = beanFactory != null? beanFactory.getBean(FeignContext.class): applicationContext.getBean(FeignContext.class);    // 这块是关键,构建了一个Feign对象,在这里设置feign具备哪些功能    Feign.Builder builder = feign(context);    //没有配置url的时候走下面的流程    if (!StringUtils.hasText(url)) {      if (!name.startsWith("http")) {        url = "http://" + name;      }      else {        url = name;      }      url += cleanPath();                  return (T) loadBalance(builder, context,new HardCodedTarget<>(type, name, url));    }    if (StringUtils.hasText(url) && !url.startsWith("http")) {      url = "http://" + url;    }    String url = this.url + cleanPath();    Client client = getOptional(context, Client.class);    if (client != null) {      if (client instanceof LoadBalancerFeignClient) {        // not load balancing because we have a url,        // but ribbon is on the classpath, so unwrap        client = ((LoadBalancerFeignClient) client).getDelegate();      }      if (client instanceof FeignBlockingLoadBalancerClient) {        // not load balancing because we have a url,        // but Spring Cloud LoadBalancer is on the classpath, so unwrap        client = ((FeignBlockingLoadBalancerClient) client).getDelegate();      }      if (client instanceof RetryableFeignBlockingLoadBalancerClient) {        // not load balancing because we have a url,        // but Spring Cloud LoadBalancer is on the classpath, so unwrap        client = ((RetryableFeignBlockingLoadBalancerClient) client)            .getDelegate();      }      builder.client(client);    }    Targeter targeter = get(context, Targeter.class);    return (T) targeter.target(this, builder, context,        new HardCodedTarget<>(type, name, url));  }  protected Feign.Builder feign(FeignContext context) {    FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);    Logger logger = loggerFactory.create(type);
// @formatter:off Feign.Builder builder = get(context, Feign.Builder.class) // required values .logger(logger) .encoder(get(context, Encoder.class)) .decoder(get(context, Decoder.class)) .contract(get(context, Contract.class)); // @formatter:on    // 关键点   configureFeign(context, builder); applyBuildCustomizers(context, builder);
return builder; }  protected void configureFeign(FeignContext context, Feign.Builder builder) {      //配置Feign configureUsingConfiguration(context, builder);      //从属性中获取配feign的属性      configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()),builder);      configureUsingProperties(properties.getConfig().get(contextId), builder); } protected void configureUsingConfiguration(FeignContext context, Feign.Builder builder) {      主要包含:      1,日志级别      2,Retryer      3,ErrorDecoder      4,Request.Options      5,RequestInterceptor      6,QueryMapEncoder      7,ExceptionPropagationPolicy 异常策略  }  protected <T> T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget<T> target) { Client client = getOptional(context, Client.class); if (client != null) { builder.client(client); //Targeter是HystrixTargeter Targeter targeter = get(context, Targeter.class);      //具体看HystrixTargeter.target 方法 return targeter.target(this, builder, context, target); }    //看这个自定义异常就能发现Client是spring-cloud-starter-netflix-ribbon or spring-cloud-starter-loadbalancer这两个里面的 throw new IllegalStateException( "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon or spring-cloud-starter-loadbalancer?"); } }class HystrixTargeter implements Targeter { public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context, Target.HardCodedTarget<T> target) { return feign.target(target);      }}public abstract class Feign {  //构建了Feign的默认参数 public static class Builder {   private InvocationHandlerFactory invocationHandlerFactory = new InvocationHandlerFactory.Default(); }  public <T> T target(Target<T> target) {     return build().newInstance(target);  }  public Feign build() {   SynchronousMethodHandler.Factory synchronousMethodHandlerFactory = new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger, logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding); ParseHandlersByName handlersByName = new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder, errorDecoder, synchronousMethodHandlerFactory);     //invocationHandlerFactory 在Builder中构建了默认InvocationHandlerFactory.Default()     return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);  }}public class ReflectiveFeign extends Feign { public <T> T newInstance(Target<T> target) {     // targetToHandlersByName内构建了SynchronousMethodHandler和BuildTemplateByResolvingArgs Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);     // factory 就是在Feign.build传入的,是Feign.Builder初始化的     //在此create的时候ReflectiveFeign.FeignInvocationHandler(target, dispatch);     InvocationHandler handler = factory.create(target, methodToHandler);     //最后代理的handler是FeignInvocationHandler     T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[] {target.type()}, handler);     return proxy; }  static class FeignInvocationHandler implements InvocationHandler {   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {   }  }}//FeignClient 没有配置url,只有一个服务名@FeignClient("demo-service")public interface DemoServiceFeignClient { @RequestMapping(value = "/demo/hello",method = {RequestMethod.GET}) ResultBean hello();
}

综上所述,在从FeignClientFactoryBean.getObject的时候,根据配置参数构建了一个Feign。

整个feign对象的生成流程如下:



这里又有两个点:

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

2,Targeter用哪个?

回过头来,继续看

  protected <T> T loadBalance(Feign.Builder builder, FeignContext context,      HardCodedTarget<T> target) {    //从spring的context中获取指定类型Client的实例    Client client = getOptional(context, Client.class);    if (client != null) {      builder.client(client);      //从spring的context中获取指定类型Targeter的实例      //Targeter是HystrixTargeter      Targeter targeter = get(context, Targeter.class);      return targeter.target(this, builder, context, target);    }    //看这个自定义异常就能发现Client是spring-cloud-starter-netflix-ribbon or spring-cloud-starter-loadbalancer这两个里面的    throw new IllegalStateException(        "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon or spring-cloud-starter-loadbalancer?");  }

其中

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 的注入有了是LoadBalancerFeignClient。

我们再看

@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。

我们再看

 protected <T> T loadBalance(Feign.Builder builder, FeignContext context,      HardCodedTarget<T> target) {    Client client = getOptional(context, Client.class);    if (client != null) {      builder.client(client);      Targeter targeter = get(context, Targeter.class);      //最后返回的是targeter.target      return targeter.target(this, builder, context, target);    }    //看这个自定义异常就能发现Client是spring-cloud-starter-netflix-ribbon or spring-cloud-starter-loadbalancer这两个里面的    throw new IllegalStateException(        "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon or spring-cloud-starter-loadbalancer?");  }  //具体实现class HystrixTargeter implements Targeter {
@Override public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context, Target.HardCodedTarget<T> target) {     //最终走的是feign.target() return feign.target(target); }}public abstract class Feign { public <T> T target(Target<T> target) {      //build()构建了一个ReflectiveFeign,然后执行newInstance return build().newInstance(target); }    public Feign build() {      return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);    }}public class ReflectiveFeign extends Feign {    public <T> T newInstance(Target<T> target) {     InvocationHandler handler = factory.create(target, methodToHandler); T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[] {target.type()}, handler); for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) { defaultMethodHandler.bindTo(proxy); } return proxy;    }}


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

然后我们断点验证下:

确实如分析所说。

Feign生成流程分析完毕。

接下来我们分析下Feign使用流程

    @Autowired    private DemoServiceFeignClient demoServiceFeginClient;    @GetMapping("hello")    public ResultBean hello(){        String msg = "api进入的版本号是:"+version;        log.info(msg);        return demoServiceFeginClient.hello();    }

我们知道Autowired是通过类型查找的,所以这块一定会找到一个DemoServiceFeignClient的Proxy对象,Proxy的handler是FeignInvocationHandler

我们梳理下我们从上面得到的信息:

Feign使用的是: ReflectiveFeignProxy的源也是: ReflectiveFeignProxy的handler是: ReflectiveFeign.FeignInvocationHandlerhandler 持有的dispatch是:SynchronousMethodHandlertarget是:HardCodedTargetclient是:LoadBalancerFeignClient

我们看下handler的源码

static class FeignInvocationHandler implements InvocationHandler {    //要执行的对象    private final Target target;    //方法和SynchronousMethodHandler的映射集合    private final Map<Method, MethodHandler> dispatch;    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {       //具体执行的的是dispatch的invoke       return dispatch.get(method).invoke(args);    }}final class SynchronousMethodHandler implements MethodHandler {  public Object invoke(Object[] argv) throws Throwable {    //构建一个RequestTemplate由ReflectiveFeign.BuildEncodedTemplateFromArgs,主要是解析    RequestTemplate template = buildTemplateFromArgs.create(argv);    //超时时间,链接时间配置获取,没有就走默认    Options options = findOptions(argv);    Retryer retryer = this.retryer.clone();    while (true) {      try {        //关键在这里        return executeAndDecode(template, options);      } catch (RetryableException e) {        try {          retryer.continueOrPropagate(e);        } catch (RetryableException th) {        }        continue;      }    }  }  Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {    //执行所有RequestInterceptor并构建一个Request对象    Request request = targetRequest(template);    Response response;    long start = System.nanoTime();    try {      //client是LoadBalancerFeignClient,我们看下源码      response = client.execute(request, options);      // ensure the request is set. TODO: remove in Feign 12      response = response.toBuilder()          .request(request)          .requestTemplate(template)          .build();    } catch (IOException e) {      throw errorExecuting(request, e);    }    long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
CompletableFuture<Object> resultFuture = new CompletableFuture<>(); asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response,        metadata.returnType(),elapsedTime);    return resultFuture.join(); }  Request targetRequest(RequestTemplate template) {    //遍历所有的拦截器并执行 for (RequestInterceptor interceptor : requestInterceptors) { interceptor.apply(template); }    //最终在return Request.create(this.method, this.url(), this.headers(), this.body, this); return target.apply(template); }}public final class RequestTemplate implements Serializable { public Request request() { if (!this.resolved) { throw new IllegalStateException("template has not been resolved."); } return Request.create(this.method, this.url(), this.headers(), this.body, this); }}public class LoadBalancerFeignClient implements Client { public Response execute(Request request, Request.Options options) throws IOException { try { URI asUri = URI.create(request.url()); String clientName = asUri.getHost(); URI uriWithoutHost = cleanUrl(request.url(), clientName);      //构建RibbonRequest FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest( this.delegate, request, uriWithoutHost);      // 获取配置信息 IClientConfig requestConfig = getClientConfig(options, clientName);      //找到      return lbClient(clientName).executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();    } }  private FeignLoadBalancer lbClient(String clientName) { return this.lbClientFactory.create(clientName); }}
public class CachingSpringLoadBalancerFactory { public FeignLoadBalancer create(String clientName) {    // 缓存有直接返回 FeignLoadBalancer client = this.cache.get(clientName); if (client != null) { return client; } IClientConfig config = this.factory.getClientConfig(clientName);    //获取ILoadBalancer实例 ILoadBalancer lb = this.factory.getLoadBalancer(clientName); ServerIntrospector serverIntrospector = this.factory.getInstance(clientName, ServerIntrospector.class);    //构建一个FeignLoadBalancer client = this.loadBalancedRetryFactory != null ? new RetryableFeignLoadBalancer(lb, config, serverIntrospector, this.loadBalancedRetryFactory) : new FeignLoadBalancer(lb, config, serverIntrospector);    //缓存起来 this.cache.put(clientName, client); return client; }}
FeignLoadBalancer 继承了AbstractLoadBalancerAwareClient,最终executeWithLoadBalancer
public abstract class AbstractLoadBalancerAwareClient<S extends ClientRequest, T extends IResponse> extends LoadBalancerContext implements IClient<S, T>, IClientConfigAware { public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {        LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig); try { return command.submit( new ServerOperation<T>() { @Override public Observable<T> call(Server server) {                        // 这个时候从注册中心里获取到了真实的调用url URI finalUri = reconstructURIWithServer(server, request.getUri()); S requestForServer = (S) request.replaceUri(finalUri); try { //执行调用 return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig)); } catch (Exception e) { return Observable.error(e); } } }) .toBlocking() .single(); } catch (Exception e) { Throwable t = e.getCause(); if (t instanceof ClientException) { throw (ClientException) t; } else { throw new ClientException(e); }        } }}

看下具体的执行流程


至于怎么去做负载,后期再分析。


原文作者:5ycode;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论

注册时间:2021-03-26

  • 博文量
    124
  • 访问量
    71509