Feign - 源碼分析

例子此次先不寫了。。。直接源碼走起。部分設計跟Ribbon同樣,這邊就不在累述,建議先看Ribbon系列。
依然從spring.factories提及。注意到這裏有這幾個類:FeignAutoConfiguration、FeignRibbonClientAutoConfiguration。spring

啓動

FeignAutoConfiguration

加載FeignContext,這裏會賦值FeignClientSpecification的集合,
後面說FeignClientSpecification類怎麼來的,其實跟Ribbon用的是同一個方法。
FeignContext的defaultConfigType是FeignClientsConfiguration.class,這個和Ribboon用法同樣,後面會加載。segmentfault

@Autowired(required = false)
private List<FeignClientSpecification> configurations = new ArrayList<>();
@Bean
public FeignContext feignContext() {
    FeignContext context = new FeignContext();
    context.setConfigurations(this.configurations);
    return context;
}

另外一個加載的是HystrixTargeter,雖然是HystrixTargeter,若是Feign.Builder不是feign.hystrix.HystrixFeign.Builder,後面調用的仍是Feign.Builder的target方法。app

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
protected static class HystrixFeignTargeterConfiguration {

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

}

FeignRibbonClientAutoConfiguration

加載CachingSpringLoadBalancerFactory,這個類會注入SpringClientFactory,看過了Ribbon,是否是就知道了他要作什麼。沒錯,他會建立一個ILoadBalancer,用於負載均衡。負載均衡

@Bean
@Primary
@ConditionalOnMissingBean
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
public CachingSpringLoadBalancerFactory cachingLBClientFactory(
        SpringClientFactory factory) {
    return new CachingSpringLoadBalancerFactory(factory);
}

FeignRibbonClientAutoConfiguration還import了DefaultFeignLoadBalancedConfiguration類,在這個類,會建立一個LoadBalancerFeignClient,後面須要的Client就是他了。ide

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

image.png

@EnableFeignClients

咱們使用feign的時候,都會使用這個註解,註解裏Import了FeignClientsRegistrar這個類,FeignClientsRegistrar實現了ImportBeanDefinitionRegistrar接口,調用registerBeanDefinitions方法。這個方法作了兩件事,一件事是註冊FeignClientSpecification類,後面會注入到上面的FeignContext中,這個用法跟Ribbon - 幾種自定義負載均衡策略提的同樣。另一件事就是建立FactoryBean。ui

@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
        BeanDefinitionRegistry registry) {
    // 註冊FeignClientSpecification類
    registerDefaultConfiguration(metadata, registry);
    // 經過註解的信息註冊FeignClientFactoryBean類,
    //這個是FactoryBean,因此容器fresh的時候會調用他的getObject方法
    registerFeignClients(metadata, registry);
}

這個方法就會掃描EnableFeignClients和FeignClient的註解,而後註冊FeignClientFactoryBean類型的bean,這個是FactoryBean,因此容器fresh的時候會調用他的getObject方法。this

public void registerFeignClients(AnnotationMetadata metadata,
        BeanDefinitionRegistry registry) {

    LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
    // 掃描EnableFeignClients註解
    Map<String, Object> attrs = metadata
            .getAnnotationAttributes(EnableFeignClients.class.getName());
        AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
            FeignClient.class);
    final Class<?>[] clients = attrs == null ? null
            : (Class<?>[]) attrs.get("clients");
    // 若是EnableFeignClients沒有配置clients信息,掃描FeignClient註解
    if (clients == null || clients.length == 0) {
        ClassPathScanningCandidateComponentProvider scanner = getScanner();
        scanner.setResourceLoader(this.resourceLoader);
        // 掃描FeignClient註解
        scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
        Set<String> basePackages = getBasePackages(metadata);
        for (String basePackage : basePackages) {
            candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
        }
    }
    else {
        // 若是配置clients信息,就忽略FeignClient註解,直接讀取clients信息
        for (Class<?> clazz : clients) {
            candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
        }
    }

    for (BeanDefinition candidateComponent : candidateComponents) {
        if (candidateComponent instanceof AnnotatedBeanDefinition) {
            // verify annotated class is an interface
            AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
            AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
            Assert.isTrue(annotationMetadata.isInterface(),
                    "@FeignClient can only be specified on an interface");

            Map<String, Object> attributes = annotationMetadata
                    .getAnnotationAttributes(FeignClient.class.getCanonicalName());

            String name = getClientName(attributes);
            registerClientConfiguration(registry, name,
                    attributes.get("configuration"));
            // 註冊FeignClientFactoryBean
            registerFeignClient(registry, annotationMetadata, attributes);
        }
    }
}

FeignClientFactoryBean#getObject

getObject會調用getTarget方法,他會建立一個Feign.Builder,建立的時候會傳FeignContext,這個FeignContext的加載在上面已經提過了。而後調用loadBalance方法返回代理對象,這個代理就是後面遠程調用用的。編碼

<T> T getTarget() {
    FeignContext context = applicationContext.getBean(FeignContext.class);
    Feign.Builder builder = feign(context);

    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));
    }
    // 直接ip的形式,代碼略
}

FeignClientFactoryBean#feign

建立一個Feign.Builder,FeignContext#getInstance,這個類繼承了NamedContextFactory,流程和ribbon的SpringClientFactory#getContext同樣。上面提到FeignContext的defaultConfigType是FeignClientsConfiguration.class,因此還會加載OptionalDecoder解碼、SpringEncoder編碼、SpringMvcContract,解析MVC註解、NEVER_RETRY重試、DefaultFeignLoggerFactory日誌、FeignClientConfigurer、Feign.builder()。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
    // 整合配置Feign.Builder
    configureFeign(context, builder);

    return builder;
}

FeignClientFactoryBean#loadBalance

這裏獲取Client和Targeter類,Client就是上面的LoadBalancerFeignClient,他是全部共享的。Targeter是HystrixTargeter,他是serviceId私有的。上面已經提了他最終是調用Feign.Builder#target。spa

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);
        return targeter.target(this, builder, context, target);
    }
    // 異常略
}

Feign.Builder#target

build方法是構建一個ReflectiveFeign對象,newInstance是生成代理對象。

public <T> T target(Target<T> target) {
  return build().newInstance(target);
}

build方法中,ReflectiveFeign建立的時候傳了ParseHandlersByName,InvocationHandlerFactory,SynchronousMethodHandler三個對象。

public Feign build() {
  // 其餘略
  return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}

代理對象生成,生成後,後面對代理對象的訪問,都會調用FeignInvocationHandler#invoke方法。

public <T> T newInstance(Target<T> target) {
    // ParseHandlersByName是經過Contrac用來解析接口定義。
    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    // 給每一個方法建立一個SynchronousMethodHandler對象
    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();

    for (Method method : target.type().getMethods()) {
      if (method.getDeclaringClass() == Object.class) {
        continue;
      } else if (Util.isDefault(method)) {
        DefaultMethodHandler handler = new DefaultMethodHandler(method);
        defaultMethodHandlers.add(handler);
        methodToHandler.put(method, handler);
      } else {
        methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
      }
    }
    // 建立一個InvocationHandler對象,這裏是FeignInvocationHandler
    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;
}

總結

啓動的時候,爲每一個接口,生成代理對象。
image

方法調用

FeignInvocationHandler#invoke

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // 其餘略
    //dispatch.get(method)獲取的是SynchronousMethodHandler
    return dispatch.get(method).invoke(args);
}

SynchronousMethodHandler#invoke

調用LoadBalancerFeignClient#execute,LoadBalancerFeignClient加載上面已經講過了。

public Object invoke(Object[] argv) throws Throwable {
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    // 其餘略
    return executeAndDecode(template, options);
   // 其餘略
}

Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
    // 其餘略
    response = client.execute(request, options);
    // 其餘略
}

LoadBalancerFeignClient#execute

@Override
public Response execute(Request request, Request.Options options) throws IOException {
    try {
        // 獲取Uri
        URI asUri = URI.create(request.url());
        // 獲取serviceId
        String clientName = asUri.getHost();
        URI uriWithoutHost = cleanUrl(request.url(), clientName);
        FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
                this.delegate, request, uriWithoutHost);
        
        IClientConfig requestConfig = getClientConfig(options, clientName);
        // 獲取FeignLoadBalancer,經過FeignLoadBalancer調用請求
        return lbClient(clientName)
                .executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
    }
    catch (ClientException e) {
        IOException io = findIOException(e);
        if (io != null) {
            throw io;
        }
        throw new RuntimeException(e);
    }
}

lbClientFactory是CachingSpringLoadBalancerFactory,因此調用CachingSpringLoadBalancerFactory#create的create方法。
CachingSpringLoadBalancerFactory有個熟悉的SpringClientFactory對象,他負責獲取ILoadBalancer、IClientConfig、ServerIntrospector,而後經過這三個構建一個FeignLoadBalancer對象。既然獲得了ILoadBalancer,那後續負載均衡的部分就再也不繼續了,參考Ribbon - 負載均衡流程

private FeignLoadBalancer lbClient(String clientName) {
    return this.lbClientFactory.create(clientName);
}

public FeignLoadBalancer create(String clientName) {
    FeignLoadBalancer client = this.cache.get(clientName);
    if (client != null) {
        return client;
    }
    IClientConfig config = this.factory.getClientConfig(clientName);
    ILoadBalancer lb = this.factory.getLoadBalancer(clientName);
    ServerIntrospector serverIntrospector = this.factory.getInstance(clientName,
            ServerIntrospector.class);
    client = this.loadBalancedRetryFactory != null
            ? new RetryableFeignLoadBalancer(lb, config, serverIntrospector,
                    this.loadBalancedRetryFactory)
            : new FeignLoadBalancer(lb, config, serverIntrospector);
    this.cache.put(clientName, client);
    return client;
}

總結

主要是經過代理對象,而後調用Ribbon進行負載均衡。咱們這邊只講述了主流程,怎麼構建HTTP請求,怎麼處理返回結果,都略過了。。。。。
image

相關文章
相關標籤/搜索