Feign 调用过程分析

概述

SpringCloud feign 对Netflix feign进行了包装和增强,本文从源码角度梳理一个请求链路中参与的各个组件及其它们如何配合交互完成请求。组件包括feign、ribbon、hystrix、sleuth等

核心类及流程

主要流程

上图为部分流程图,大致可以分为以下3个步骤

  • 元数据的注册
    FeignClient配置及BeanDefinition的注册等
  • Feign初始化
    Feign的上下文信息、负载均衡代理、Feign代理的实例化等
  • 请求调用
    接受请求,进行负载均衡,调用http client客户端完成请求调用等

源码分析

从上文中的几个方面来解读源码

元数据的注册

从EnableXXX开始 -> @EnableFeignClients:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class) 
public @interface EnableFeignClients {
    ...
}
//通过import方式,引入FeignClientsRegistrar,处理逻辑在这
class FeignClientsRegistrar
        implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
        //1. 先注册默认配置
        registerDefaultConfiguration(metadata, registry);
        //2. 注册所有的feignClient BeanDefinition
        registerFeignClients(metadata, registry);
    }
}

实现了ImportBeanDefinitionRegistrar接口,开始进行BeanDefinition的注入,分别看下registerBeanDefinitions中的两个方法

//注册默认配置
private void registerDefaultConfiguration(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
        Map<String, Object> defaultAttrs = metadata
                .getAnnotationAttributes(EnableFeignClients.class.getName(), true);

        if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
            String name;
            if (metadata.hasEnclosingClass()) {
                name = "default." + metadata.getEnclosingClassName();
            }
            else {
                name = "default." + metadata.getClassName();
            }
            //以default为前缀,注册feignClient configuration 第一处
            registerClientConfiguration(registry, name,
                    defaultAttrs.get("defaultConfiguration"));
        }
    }

上述方法读取@EnableFeignClients注解中声明的defaultConfiguration配置,name以default为前缀,一般情况不需要配置。

//注册所有的FeignClient 的BeanDefinition
public void registerFeignClients(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
        ...
        //获取扫描目录下面所有的bean deanDefinition  
        for (String basePackage : basePackages) {
            Set<BeanDefinition> candidateComponents = scanner
                    .findCandidateComponents(basePackage);
            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);
                   //注册feignClient configuration 第二处
                    registerClientConfiguration(registry, name,
                            attributes.get("configuration"));
                    //注册feignClient
                    registerFeignClient(registry, annotationMetadata, attributes);
                }
            }
        }
    }

上述两个代码片段中都调用了registerClientConfiguration,注册了FeignClientSpecification,这个类很重要,是feign client配置的载体,包含FeignClient的核心配置重试策略、超时策略、日志等配置。某个服务没有设置就读取默认(default)的配置。再看看单个FeignClient的注册registerFeignClient

private void registerFeignClient(BeanDefinitionRegistry registry,
            AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
        String className = annotationMetadata.getClassName();
        //注册FeignClientFactoryBean
        BeanDefinitionBuilder definition = BeanDefinitionBuilder
                .genericBeanDefinition(FeignClientFactoryBean.class);
        // ... 装配@FeignClient定义的元数据

        BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
                new String[] { alias });
        BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    }

扫描到所有@FeignClient的BeanDefinition后,包装成FeignClientFactoryBean,然后注册到spring上下文中。FeignClientFactoryBean是后续处理的关键,它是一个FactoryBean,也即一个代理,在从spring容器中注入时进行被代理对象的处理。到此配置信息及BeanDefinition注册结束。

Feign初始化

承接上文,直接分析FeignClientFactoryBean,这个类是Feign初始化的最核心的类,先整体看下代码结构

    @Override
    public Object getObject() throws Exception {
        return getTarget();
    }

    <T> T getTarget() {
        //实例化FeignContext
        FeignContext context = this.applicationContext.getBean(FeignContext.class);
       //实例化Feign.Builder,用来生成Feign
        Feign.Builder builder = feign(context);
       //判断生成代理对象的url, 如果为空,走负载均衡,生成负载均衡的代理类
        if (!StringUtils.hasText(this.url)) {
            if (!this.name.startsWith("http")) {
                this.url = "http://" + this.name;
            }
            else {
                this.url = this.name;
            }
            this.url += cleanPath();
            //负载均衡
            return (T) loadBalance(builder, context,
                    new HardCodedTarget<>(this.type, this.name, this.url));
        }
        if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
            this.url = "http://" + this.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();
            }
            builder.client(client);
        }
        Targeter targeter = get(context, Targeter.class);
                //生成默认代理类,不走负载均衡直连
        return (T) targeter.target(this, builder, context,
                new HardCodedTarget<>(this.type, this.name, url));
    }

FeignClientFactoryBean 完成了调用前的所有准备工作,如FeignContext,Feign.builder的创建、判断是否需要负载均衡及设置及Feign的代理类的创建等,下文逐个说明

第一步 实例化Feign上下文FeignContext

FeignContext是Feign初始化需要的上下信息,通过FeignAutoConfiguration引入

@Configuration
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({ FeignClientProperties.class,
        FeignHttpClientProperties.class })
public class FeignAutoConfiguration {

    @Autowired(required = false)
    private List<FeignClientSpecification> configurations = new ArrayList<>();

    @Bean
    public HasFeatures feignFeature() {
        return HasFeatures.namedFeature("Feign", Feign.class);
    }

    @Bean
    public FeignContext feignContext() {
        FeignContext context = new FeignContext();
        context.setConfigurations(this.configurations);
        return context;
    }

}

FeignAutoConfiguration是Feign 配置的第一个入口,在这里上文中注册的FeignClientSpecification派上了用场,注入到了FeignClientSpecification集合,顺便提一下Spring支持集合类型对象的注入如List,Map等。在没有引入Sleuth时会注入FeignContext,引入Sleuth之后呢?再看下TraceFeignClientAutoConfiguration:

@Configuration
@ConditionalOnProperty(value = "spring.sleuth.feign.enabled", matchIfMissing = true)
@ConditionalOnClass({ Client.class, FeignContext.class })
@ConditionalOnBean(HttpTracing.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
@AutoConfigureAfter({ SleuthHystrixAutoConfiguration.class,
        TraceHttpAutoConfiguration.class })
public class TraceFeignClientAutoConfiguration {

    @Configuration
    @ConditionalOnProperty(name = "spring.sleuth.feign.processor.enabled", matchIfMissing = true)
    protected static class FeignBeanPostProcessorConfiguration {

        //引入Sleuth后,初始化FeignContextBeanPostProcessor
        @Bean
        static FeignContextBeanPostProcessor feignContextBeanPostProcessor(
                BeanFactory beanFactory) {
            return new FeignContextBeanPostProcessor(beanFactory);
        }

    }
}

final class FeignContextBeanPostProcessor implements BeanPostProcessor {
    //PostProcessor 将FeignContext包装成TraceFeignContext
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName)
            throws BeansException {
        if (bean instanceof FeignContext && !(bean instanceof TraceFeignContext)) {
            return new TraceFeignContext(traceFeignObjectWrapper(), (FeignContext) bean);
        }
        return bean;
    }
}

@AutoConfigureBefore(FeignAutoConfiguration.class),保证了在FeignAutoConfiguration前初始化,这里定义了一个BeanPostProcessor, 将FeignContext包装成TranceFeignContext,加上了trace的配置信息

第二步 实例化Feign.Builder

Builder是Feign的构造器,进行Feign属性的设置,方便后续Feign对象的创建。回到主类FeignClientFactoryBean中的feign()方法

   protected Feign.Builder feign(FeignContext context) {
        FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
        Logger logger = loggerFactory.create(this.type);

        // 从FeignContext获取Feign.Builder
        // @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);

        return builder;
    }
    //从FeignContext中获取配置信息
    protected <T> T get(FeignContext context, Class<T> type) {
        T instance = context.getInstance(this.contextId, type);
        if (instance == null) {
            throw new IllegalStateException(
                    "No bean found of type " + type + " for " + this.contextId);
        }
        return instance;
    }

从FeignContext中获取上下文信息来实例化Feign.Builder。回到上文中的FeignContext的创建过程,构造函数中一个入参FeignClientsConfiguration,这个类就是FeignClient的属性配置类,源码如下:

public class FeignContext extends NamedContextFactory<FeignClientSpecification> {
    //构造函数,指定了FeignClientsConfiguration
    public FeignContext() {
        super(FeignClientsConfiguration.class, "feign", "feign.client.name");
    }

}

public class FeignClientsConfiguration {
    //其他属性设置
    ...

    //缺省设置,直接使用Feign.Builder
    @Bean
    @Scope("prototype")
    @ConditionalOnMissingBean
    public Feign.Builder feignBuilder(Retryer retryer) {
        return Feign.builder().retryer(retryer);
    }

    //引入hystrix后,使用HystrixFeign.Builder
    @Configuration
    @ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
    protected static class HystrixFeignConfiguration {

        @Bean
        @Scope("prototype")
        @ConditionalOnMissingBean
        @ConditionalOnProperty(name = "feign.hystrix.enabled")
        public Feign.Builder feignHystrixBuilder() {
            return HystrixFeign.builder();
        }

    }

}

引入Sleuth又会如何变化呢?继续深入看下上文中的TraceFeignClientAutoConfiguration,有如下片段

public class TraceFeignClientAutoConfiguration {

    @Bean
    @Scope("prototype")
    @ConditionalOnClass(name = { "com.netflix.hystrix.HystrixCommand",
            "feign.hystrix.HystrixFeign" })
    @ConditionalOnProperty(name = "feign.hystrix.enabled", havingValue = "true")
    Feign.Builder feignHystrixBuilder(BeanFactory beanFactory) {
        return SleuthHystrixFeignBuilder.builder(beanFactory);
    }

    @Bean
    @ConditionalOnMissingBean
    @Scope("prototype")
    @ConditionalOnProperty(name = "feign.hystrix.enabled", havingValue = "false", matchIfMissing = true)
    Feign.Builder feignBuilder(BeanFactory beanFactory) {
        return SleuthFeignBuilder.builder(beanFactory);
    }
}

又出现了两个Builder, SleuthHystrixFeignBuilder、SleuthFeignBuilder,分别适用于有Hystrix和无Hystrix。跟进后会发现,它们内部仍然使用上述的两个Builder,Feign.Builder和HystrixFeign.Builder。更多的是将Client包装成了Sleuth的LazyClient。

第三步 实例化负载均衡代理

ribbon开始登场,继续回到主类FeignClientFactoryBean

//1. 负载入口
protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
            HardCodedTarget<T> target) {
        //从FeignContext中获取Client
        Client client = getOptional(context, Client.class);
        if (client != null) {
            builder.client(client);
                //从FeignContext或获取Targeter
            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-netflix-ribbon?");
}
//2. 负载配置入口
@ConditionalOnClass({ ILoadBalancer.class, Feign.class })
@Configuration
@AutoConfigureBefore(FeignAutoConfiguration.class)
@EnableConfigurationProperties({ FeignHttpClientProperties.class })
// Order is important here, last should be the default, first should be optional
// see
// https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
@Import({ HttpClientFeignLoadBalancedConfiguration.class,
        OkHttpFeignLoadBalancedConfiguration.class,
        DefaultFeignLoadBalancedConfiguration.class })
public class FeignRibbonClientAutoConfiguration {

    ...
}
//3. 默认配置
class DefaultFeignLoadBalancedConfiguration {

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

}

FeignContext中获取的Client, Client在FeignRibbonClientAutoConfiguration中自动配置,代码中@Import中的XXXFeignLoadBalancedConfiguration根据classpath中Http Client完成LoadBalancerFeignClient的实例化。从XXX名字可以看出, 分别对应Apache HttpClient、 OkHttpClient和Java HttpClient(默认)。这里建议使用Apache HttpClient或OkHttpClient,有线程池支持,性能更好。加上sleuth的支持又如何变化?看一看增强后的 TranceFeignContext

class TraceFeignContext extends FeignContext {

    @Override
    @SuppressWarnings("unchecked")
    public <T> Map<String, T> getInstances(String name, Class<T> type) {
        //从基类FeignContext中获取属性集合
        Map<String, T> instances = this.delegate.getInstances(name, type);
        if (instances == null) {
            return null;
        }
        Map<String, T> convertedInstances = new HashMap<>();
        //逐个遍历属性,进行wrap
        for (Map.Entry<String, T> entry : instances.entrySet()) {
            convertedInstances.put(entry.getKey(),
                    (T) this.traceFeignObjectWrapper.wrap(entry.getValue()));
        }
        return convertedInstances;
    }

    //把Client包装成TraceLoadBalancerFeignClient
    Object wrap(Object bean) {
        if (bean instanceof Client && !(bean instanceof TracingFeignClient)) {
            if (ribbonPresent && bean instanceof LoadBalancerFeignClient
                    && !(bean instanceof TraceLoadBalancerFeignClient)) {
                LoadBalancerFeignClient client = ((LoadBalancerFeignClient) bean);
                return new TraceLoadBalancerFeignClient(
                        (Client) new TraceFeignObjectWrapper(this.beanFactory)
                                .wrap(client.getDelegate()),
                        factory(), (SpringClientFactory) clientFactory(),
                        this.beanFactory);
            }
            else if (ribbonPresent && bean instanceof TraceLoadBalancerFeignClient) {
                return bean;
            }
            return new LazyTracingFeignClient(this.beanFactory, (Client) bean);
        }
        return bean;
    }
}

TraceFeignContext 把FeignClient返回的Client包装成TraceLoadBalancerFeignClient。注意在没有引入ribbon、sleuth时Client直接使用默认对象Client.Default。 至此负载均衡的client代理就完成了创建。

第四步 实例化Feign代理

继续分析FeignClientFactoryBean, 有如下片段

Targeter targeter = get(context, Targeter.class);
return targeter.target(this, builder, context, target);

这里的Targeter是用来生成ReflectiveFeign(Feign的实现类)的工具类,有如下两种实现,再次回到FeignAutoConfiguration

public class FeignAutoConfiguration {

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

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

    @Configuration
    @ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
    protected static class DefaultFeignTargeterConfiguration {

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

默认使用DefaultTargeter, 引入hystrix后使用HystrixTargeter。继续跟进会发现最终是在实例化Feign代理

public abstract class Feign {
    // Targeter 调用的地方
    public <T> T target(Target<T> target) {
      return build().newInstance(target);
    }
    //实例化Feign实现类并填充SynchronousMethodHandler.Factory
    public Feign build() {
      SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
          new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
              logLevel, decode404, closeAfterDecode, propagationPolicy);
      ParseHandlersByName handlersByName =
          new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
              errorDecoder, synchronousMethodHandlerFactory);
      return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
    }
}

public class ReflectiveFeign extends Feign {

  //创建Feign代理类
  @Override
  public <T> T newInstance(Target<T> target) {
    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();

    //将Feign Client 接口中的所有方法,包装成MethodHandler
    for (Method method : target.type().getMethods()) {
      if (method.getDeclaringClass() == Object.class) {
        continue;
      } else if (Util.isDefault(method)) {//判断方法是否是默认方法,启用默认handler
        DefaultMethodHandler handler = new DefaultMethodHandler(method);
        defaultMethodHandlers.add(handler);
        methodToHandler.put(method, handler);
      } else {
        methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
      }
    }
    //创建InvocationHandler,调用时转发到methodHandler
    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;
  }}

到这就很明朗了,使用Targeter来创建ReflectiveFeign,同时引出了SynchronousMethodHandler,这个类很重要,是发送请求的关键类,后文再叙。也可以看到动态代理的基础类InvocationHandler,InvocationHandler的最终实现有两种选择:FeignInvocationHandler(无hystrix)、HystrixInvocationHandler(有hystrix)

//1. 使用Feign.Builder 最终使用的是FeignInvocationHandler
public abstract class Feign {
  public static class Builder {
    //默认
    private InvocationHandlerFactory invocationHandlerFactory =
        new InvocationHandlerFactory.Default();
  }
}

static final class Default implements InvocationHandlerFactory {

    @Override
    public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
      return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
    }
}

//2. 使用HystrixFeign.Builder 最终使用的是HystrixInvocationHandler

    /** Configures components needed for hystrix integration. */
    Feign build(final FallbackFactory<?> nullableFallbackFactory) {
      super.invocationHandlerFactory(new InvocationHandlerFactory() {
        @Override
        public InvocationHandler create(Target target,
                                        Map<Method, MethodHandler> dispatch) {
          return new HystrixInvocationHandler(target, dispatch, setterFactory,
              nullableFallbackFactory);
        }
      });
      super.contract(new HystrixDelegatingContract(contract));
      return super.build();
    }

Feign 的动态代理创建完成,并交由Spring容器管理。第二阶段的初始化阶段至此结束。http请求发生时,创建的代理对象最终聚合ribbon、hystrix、sleuth的功能完成整个调用链路的增强或跟踪。

请求调用

上一章节的最后,创建了InvocationHandler,在请求发生时invoke将被调用。以引入hystrix场景说明,看一看HystrixInvocationHandler

HystrixInvocationHandler

final class HystrixInvocationHandler implements InvocationHandler {
  @Override
  public Object invoke(final Object proxy, final Method method, final Object[] args)
      throws Throwable {
    ...
  
    HystrixCommand<Object> hystrixCommand =
        new HystrixCommand<Object>(setterMethodMap.get(method)) {
          @Override
          protected Object run() throws Exception {
            try {
              return HystrixInvocationHandler.this.dispatch.get(method).invoke(args);
            } catch (Exception e) {
              throw e;
            } catch (Throwable t) {
              throw (Error) t;
            }
          }
          @Override
          protected Object getFallback() {

            ...
          }
         
    }
    ...

}

HystrixCommand hystrix功能的入口,其提供的fallback,熔断等都经由此入口进行增强。在run()方法中开始发起调用,找到方法对应的methodHandler,methodHandler中封装了loadBalanceClient、retryer等组件。methodHandler 上文已有提及,也就是SynchronousMethodHandler,继续跟进

final class SynchronousMethodHandler implements MethodHandler {

  @Override
  public Object invoke(Object[] argv) throws Throwable {
    //根据请求参数构建RequestTemplate
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    //定义重试策略
    Retryer retryer = this.retryer.clone();
    while (true) {
      try {
        // 重要方法在这里
        return executeAndDecode(template);
      } 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;
      }
    }
  }

  Object executeAndDecode(RequestTemplate template) throws Throwable {
    Request request = targetRequest(template);
    try {
      //在初始化阶段创建的Client在这里开始调用
      response = client.execute(request, options);
    } catch (IOException e) {
     ...
    }
    //省略响应处理
    ...
 }
}

这里已经很明确,LoadBalancerFeignClient (Ribbon)开始起作用了。继续跟进

public class LoadBalancerFeignClient implements Client {

    @Override
    public Response execute(Request request, Request.Options options) throws IOException {
        try {
            //获取URI
            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();
        }
        catch (ClientException e) {
            IOException io = findIOException(e);
            if (io != null) {
                throw io;
            }
            throw new RuntimeException(e);
        }
    }

}
    
    public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
        LoadBalancerCommand command = this.buildLoadBalancerCommand(request, requestConfig);

        try {
            return (IResponse)command.submit(new ServerOperation<T>() {
                public Observable<T> call(Server server) {
                    URI finalUri = AbstractLoadBalancerAwareClient.this.reconstructURIWithServer(server, request.getUri());
                    ClientRequest requestForServer = request.replaceUri(finalUri);

                    try {
                        // 执行ribbon负载均衡请求
                        return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
                    } catch (Exception var5) {
                        return Observable.error(var5);
                    }
                }
            }).toBlocking().single();
        } catch (Exception var6) {
            Throwable t = var6.getCause();
            if (t instanceof ClientException) {
                throw (ClientException)t;
            } else {
                throw new ClientException(var6);
            }
        }
    }

这里是ribbon发起请求的地方,至于到底选择何种http 客户端,在FeignRibbonClientAutoConfiguration中进行配置,上文已经说明。默认使用Client.Default设置的客户端

class Default implements Client {
    @Override
    public Response execute(Request request, Options options) throws IOException {
      // 这里使用的java httpclient
      HttpURLConnection connection = convertAndSend(request, options);
      return convertResponse(connection, request);
    }
}

写在最后:本文只梳理了feign如何与spring cloud生态中组件进行交互完成请求,每个组件的功能实现没有涉及,有兴趣的同学可以自行研究,特别是Hystrix和ribbon中使用的rxjava,可以参考
Hystrix核心基础 - 滑动窗口创建过程及demo

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342