OpenFeign源码

openfeign是通过FeignClientFactoryBean生成动态代理对象的方式实现http客户端无感调用,可以做到像定义接口一样写http客户端调用代码。
配置Feign接口后,我们通常会在SpringBoot项目启动类上标记@EnableFeignClients,这个是生成动态代理对象的入口。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {

   /**
    * basePackages 配置的别称
    */
   String[] value() default {};

   /**
    * 扫描包路径下有@FeignClient注解的接口
    */
   String[] basePackages() default {};

   /**
    * 扫描类对应包下有@FeignClient注解的接口
    */
   Class<?>[] basePackageClasses() default {};

   /**
    * 默认配置
    */
   Class<?>[] defaultConfiguration() default {};

   /**
    * 直接指定标记了@FeignClient的类
    */
   Class<?>[] clients() default {};

}

@EnableFeignClients里面的配置主要是为了找到@FeignClient接口,正常情况下可指定basePackages路径,或者直接不加,会直接扫描启动类及其对应包下面的所有类,去找到所有标记了@FiegnClient的接口。
这里最关键的是@Import(FeignClientsRegistrar.class)引入了FeignClientsRegistrar类。
首先看下FeignClientsRegistrar实现的接口ImportBeanDefinitionRegistrar,Spring IOC在创建bean实例的时候
会调ImportBeanDefinitionRegistrar.registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry)方法,这个方法里面是注册默认配置和注册FeignClient的逻辑。

class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
    ...
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        // 注册默认配置
        registerDefaultConfiguration(metadata, registry);
        // 注册FeignClient
        registerFeignClients(metadata, registry);
    }
}

继续看registerFeignClients(metadata, registry)方法,前面主要是通过@EnableFeignClients注解配置找到所有的Feign接口生成BeanDefinition,最后遍历根据BeanDefinition一个个注册FeignClient

public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    // 通过@EnableFeignClients的配置找到对应的Feign接口并生成对应的BeanDefinition
    LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
    Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
    final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients");
    if (clients == null || clients.length == 0) {
        ClassPathScanningCandidateComponentProvider scanner = getScanner();
        scanner.setResourceLoader(this.resourceLoader);
        scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
        Set<String> basePackages = getBasePackages(metadata);
        for (String basePackage : basePackages) {
            candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
        }
    }
    else {
        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"));

            // 注册FeignClient
            registerFeignClient(registry, annotationMetadata, attributes);
        }
    }
}

点进去registerFeignClient(registry, annotationMetadata, attributes),这个方法才是真正的注册逻辑。
可以看到方法里面主要是创建了一个FeignClientFactoryBean工厂bean,最终通过getObject()生成bean实例。

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 factoryBean = new FeignClientFactoryBean();
    factoryBean.setBeanFactory(beanFactory);
    factoryBean.setName(name);
    factoryBean.setContextId(contextId);
    factoryBean.setType(clazz);
    factoryBean.setRefreshableClient(isClientRefreshEnabled());
    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));
        }
        // 生成bean实例
        return factoryBean.getObject();
    });
    definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
    definition.setLazyInit(true);
    validate(attributes);

    AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
    beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);
    beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean);

    // has a default, won't be null
    boolean primary = (Boolean) attributes.get("primary");

    beanDefinition.setPrimary(primary);

    String[] qualifiers = getQualifiers(attributes);
    if (ObjectUtils.isEmpty(qualifiers)) {
        qualifiers = new String[] { contextId + "FeignClient" };
    }

    BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);
    BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);

    registerOptionsBeanDefinition(registry, contextId);
}

进入FeignClientFactoryBean的getObject方法,里面又继续调了getTarget方法。这里有两种情况,一种是指定了url,则会通过给的url封装请求,
如果没有则从注册中心获取服务地址,这里继续看loadBalance(builder, context, new HardCodedTarget<>(type, name, url));

public Object getObject() {
    return getTarget();
}

<T> T getTarget() {
    FeignContext context = beanFactory != null ? beanFactory.getBean(FeignContext.class)
            : applicationContext.getBean(FeignContext.class);
    Feign.Builder builder = feign(context);

    // 如果没有指定具体的url,则通过负载均衡从注册中心获取对应的服务地址信息
    if (!StringUtils.hasText(url)) {

        if (LOG.isInfoEnabled()) {
            LOG.info("For '" + name + "' URL not provided. Will try picking an instance via load-balancing.");
        }
        if (!name.startsWith("http")) {
            url = "http://" + name;
        }
        else {
            url = name;
        }
        url += cleanPath();
        return (T) loadBalance(builder, context, new HardCodedTarget<>(type, name, url));
    }
    ...
}

继续loadBalance方法解析,这里主要是从FeignContext获取到Client实例和Targeter实例,debug的话这两个实例分别对应的类是
Client -> TraceRetryableFeignBlockingLoadBalancerClient
Targeter -> DefaultTargeter

这两个类都很关键

  1. TraceRetryableFeignBlockingLoadBalancerClient是后续调用feign接口时通过RetryableFeignBlockingLoadBalancerClient获取对应的负载均衡服务实例
  2. DefaultTargeter则是生成动态代理对象
protected <T> T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget<T> target) {
    Client client = getOptional(context, Client.class);
    if (client != null) {
        builder.client(client);
        applyBuildCustomizers(context, builder);
        Targeter targeter = get(context, Targeter.class);
        // 点进去看是如何生成代理对象
        return targeter.target(this, builder, context, target);
    }

    throw new IllegalStateException(
            "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalancer?");
}

继续看动态代理对象生成的逻辑,DefaultTargeter的target方法里面,主要调用了feign.target(target)

class DefaultTargeter implements Targeter {

   @Override
   public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
         Target.HardCodedTarget<T> target) {
      return feign.target(target);
   }

}

feign.target先调用了build方法,然后再调newInstance方法。先看builde方法的逻辑,核心是创建了SynchronousMethodHandler.Factory对象,并且返回了ReflectiveFeign对象。
这个工厂对象主要是用来创建SynchronousMethodHandler的,而SynchronousMethodHandler就是feign接口每个方法都会封装一个对应的MethodHandler,里面包含了封装请求,发送请求,解析响应的所有流程。

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

public Feign build() {
  super.enrich();

  SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
      new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors,
          responseInterceptor, logger, logLevel, dismiss404, closeAfterDecode,
          propagationPolicy, forceDecoding);
  ParseHandlersByName handlersByName =
      new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
          errorDecoder, synchronousMethodHandlerFactory);
  return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}

继续看ReflectiveFeign.newInstance(Target target)方法,通过SpringMvcContract解析了feign接口相关的元信息,
SynchronousMethodHandler.Factory创建了这个类所有方法的MethodHandler,并最终通过InvocationHandlerFactory创建ReflectiveFeign.FeignInvocationHandler,最终返回动态代理对象。

public <T> T newInstance(Target<T> target) {
    // 通过SpringMvcContract解析了feign接口相关的元信息,创建MethodHandler
    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    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)));
      }
    }

    // 创建ReflectiveFeign.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;
}

至此所有的feign接口通过FeignInvocationHandler生成了动态代理对象,里面包含了feign接口所有方法的Map<Method, MethodHandler> dispatch。
后续调用feign接口只需要直接注入使用即可。动态代理对象直接对应方法时,最终会调用InvocationHandler的invoke方法。

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if ("equals".equals(method.getName())) {
    try {
      Object otherHandler =
          args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
      return equals(otherHandler);
    } catch (IllegalArgumentException e) {
      return false;
    }
    } else if ("hashCode".equals(method.getName())) {
    return hashCode();
    } else if ("toString".equals(method.getName())) {
    return toString();
    }

    // 通过方法获取到对应的SynchronousMethodHandler执行
    return dispatch.get(method).invoke(args);
}

在调用feign接口方法时,最终会执行SynchronousMethodHandler的invoke方法。

  1. 封装请求,执行请求,解码
  2. 如果抛RetryableException异常,如果不配置Retryer实例,则默认是不重试
public Object invoke(Object[] argv) throws Throwable {
    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) {
          Throwable cause = th.getCause();
          if (propagationPolicy == UNWRAP && cause != null) {
            throw cause;
          } else {
            throw th;
          }
        }
        if (logLevel != Logger.Level.NONE) {
          logger.logRetry(metadata.configKey(), logLevel);
        }
        continue;
      }
    }
}

executeAndDecode方法执行请求并且处理响应结果

Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
    Request request = targetRequest(template);

    if (logLevel != Logger.Level.NONE) {
      logger.logRequest(metadata.configKey(), logLevel, request);
    }

    Response response;
    long start = System.nanoTime();
    try {
      // 发送请求
      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) {
      if (logLevel != Logger.Level.NONE) {
        logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
      }
      throw errorExecuting(request, e);
    }
    long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);

    if (decoder != null) {
      return responseInterceptor
          .aroundDecode(new InvocationContext(decoder, metadata.returnType(), response));
    }

    CompletableFuture<Object> resultFuture = new CompletableFuture<>();
    // 处理响应结果
    asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response,
        metadata.returnType(), elapsedTime);

    try {
      if (!resultFuture.isDone())
        throw new IllegalStateException("Response handling not done");
      return resultFuture.join();
    } catch (CompletionException e) {
      Throwable cause = e.getCause();
      if (cause != null)
        throw cause;
      throw e;
    }
}

RetryableFeignBlockingLoadBalancerClient做了三件事

  1. 获取负载均衡服务实例信息
  2. 构建完整请求
  3. 发送请求并得到响应结果
public Response execute(Request request, Request.Options options) throws IOException {
    final URI originalUri = URI.create(request.url());
    String serviceId = originalUri.getHost();
    Assert.state(serviceId != null, "Request URI does not contain a valid hostname: " + originalUri);
    final LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryFactory.createRetryPolicy(serviceId,
            loadBalancerClient);
    RetryTemplate retryTemplate = buildRetryTemplate(serviceId, request, retryPolicy);
    return retryTemplate.execute(context -> {
        Request feignRequest = null;
        ServiceInstance retrievedServiceInstance = null;
        Set<LoadBalancerLifecycle> supportedLifecycleProcessors = LoadBalancerLifecycleValidator
                .getSupportedLifecycleProcessors(
                        loadBalancerClientFactory.getInstances(serviceId, LoadBalancerLifecycle.class),
                        RetryableRequestContext.class, ResponseData.class, ServiceInstance.class);
        String hint = getHint(serviceId);
        DefaultRequest<RetryableRequestContext> lbRequest = new DefaultRequest<>(
                new RetryableRequestContext(null, buildRequestData(request), hint));
        // On retries the policy will choose the server and set it in the context
        // and extract the server and update the request being made
        if (context instanceof LoadBalancedRetryContext) {
            LoadBalancedRetryContext lbContext = (LoadBalancedRetryContext) context;
            ServiceInstance serviceInstance = lbContext.getServiceInstance();
            if (serviceInstance == null) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Service instance retrieved from LoadBalancedRetryContext: was null. "
                            + "Reattempting service instance selection");
                }
                ServiceInstance previousServiceInstance = lbContext.getPreviousServiceInstance();
                lbRequest.getContext().setPreviousServiceInstance(previousServiceInstance);
                supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStart(lbRequest));

                // 负载均衡client获取到服务实例信息
                retrievedServiceInstance = loadBalancerClient.choose(serviceId, lbRequest);
                if (LOG.isDebugEnabled()) {
                    LOG.debug(String.format("Selected service instance: %s", retrievedServiceInstance));
                }
                lbContext.setServiceInstance(retrievedServiceInstance);
            }

            if (retrievedServiceInstance == null) {
                if (LOG.isWarnEnabled()) {
                    LOG.warn("Service instance was not resolved, executing the original request");
                }
                org.springframework.cloud.client.loadbalancer.Response<ServiceInstance> lbResponse = new DefaultResponse(
                        retrievedServiceInstance);
                supportedLifecycleProcessors.forEach(lifecycle -> lifecycle
                        .onComplete(new CompletionContext<ResponseData, ServiceInstance, RetryableRequestContext>(
                                CompletionContext.Status.DISCARD, lbRequest, lbResponse)));
                feignRequest = request;
            }
            else {
                if (LOG.isDebugEnabled()) {
                    LOG.debug(String.format("Using service instance from LoadBalancedRetryContext: %s",
                            retrievedServiceInstance));
                }

                // 生成带IP和端口的完整服务地址,并构建feign请求
                String reconstructedUrl = loadBalancerClient.reconstructURI(retrievedServiceInstance, originalUri)
                        .toString();
                feignRequest = buildRequest(request, reconstructedUrl);
            }
        }
        org.springframework.cloud.client.loadbalancer.Response<ServiceInstance> lbResponse = new DefaultResponse(
                retrievedServiceInstance);
        LoadBalancerProperties loadBalancerProperties = loadBalancerClientFactory.getProperties(serviceId);
        // 执行请求得到结果
        Response response = LoadBalancerUtils.executeWithLoadBalancerLifecycleProcessing(delegate, options,
                feignRequest, lbRequest, lbResponse, supportedLifecycleProcessors, retrievedServiceInstance != null,
                loadBalancerProperties.isUseRawStatusCodeInResponseData());
        int responseStatus = response.status();
        if (retryPolicy != null && retryPolicy.retryableStatusCode(responseStatus)) {
            if (LOG.isDebugEnabled()) {
                LOG.debug(String.format("Retrying on status code: %d", responseStatus));
            }
            byte[] byteArray = response.body() == null ? new byte[] {}
                    : StreamUtils.copyToByteArray(response.body().asInputStream());
            response.close();
            throw new LoadBalancerResponseStatusCodeException(serviceId, response, byteArray,
                    URI.create(request.url()));
        }
        return response;
    }, new LoadBalancedRecoveryCallback<Response, Response>() {
        @Override
        protected Response createResponse(Response response, URI uri) {
            return response;
        }
    });
}

具体的发送请求流程可以详细debug看下,所有细节列出来可能会显得太冗长。过程涉及了很多工厂模式和委托模式,自己debug以下会更加清楚一些细节。比如发送http的客户端,默认是HttpsURLConnection发送请求,LoadBalancerClientConfiguration默认会创建RoundRobinLoadBalancer轮询负载均衡策略,可以看下spring-cloud-openfeign-core下的META-INF\spring.factories自动装备类,进一步结合项目实际情况还可以自定义Encoder,Decoder,ErrorDecoder等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值