乐趣区

关于feign:Feign-源码分析

例子这次先不写了。。。间接源码走起。局部设计跟 Ribbon 一样,这边就不在累述,倡议先看 Ribbon 系列。
仍然从 spring.factories 说起。留神到这里有这几个类:FeignAutoConfiguration、FeignRibbonClientAutoConfiguration。

启动

FeignAutoConfiguration

加载 FeignContext,这里会赋值 FeignClientSpecification 的汇合,
前面说 FeignClientSpecification 类怎么来的,其实跟 Ribbon 用的是同一个办法。
FeignContext 的 defaultConfigType 是 FeignClientsConfiguration.class,这个和 Ribboon 用法一样,前面会加载。

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

@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 就是他了。

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

@EnableFeignClients

咱们应用 feign 的时候,都会应用这个注解,注解里 Import 了 FeignClientsRegistrar 这个类,FeignClientsRegistrar 实现了 ImportBeanDefinitionRegistrar 接口,调用 registerBeanDefinitions 办法。这个办法做了两件事,一件事是注册 FeignClientSpecification 类,前面会注入到下面的 FeignContext 中,这个用法跟 Ribbon – 几种自定义负载平衡策略提的一样。另外一件事就是创立 FactoryBean。

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

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()。

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。

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;
}

总结

启动的时候,为每个接口,生成代理对象。

办法调用

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 申请,怎么解决返回后果,都略过了。。。。。

退出移动版