学科分类
目录
Spring Cloud

Feign的负载均衡实现原理

Feign支持插拔式,它可以与其他组件一起使用实现特定功能。默认情况下,Feign结合了Ribbon能够实现负载均衡。

通过上一节Feign服务调用工作原理的分析,我们知道Feign封装了RequestTemplate后,通过client.execute()发起服务远程请求调用的。查看Client源码可知,Client是一个接口,它有两个实现类,分别是Defalut和LoadBalancerFeignClient,其中,LoadBalancerFeignClient类是Ribbon负载均衡客户端实现类。

那么Feign是如何注册LoadBalancerFeignClient作为其客户端调用实现的呢? 下面我们查看LoadBalancerFeignClient的自动配置类FeignRibbonClientAutoConfiguration的源码,具体代码如下所示。

 1     @ConditionalOnClass({ ILoadBalancer.class, Feign.class })
 2     @Configuration
 3     @AutoConfigureBefore(FeignAutoConfiguration.class)
 4     public class FeignRibbonClientAutoConfiguration {
 5     @Bean
 6         @ConditionalOnMissingBean
 7         public Client feignClient(CachingSpringLoadBalancerFactory 
 8                           cachingFactory,SpringClientFactory clientFactory) {
 9             return new LoadBalancerFeignClient(new Client.Default(null, null),
 10                     cachingFactory, clientFactory);
 11         }
 12     }

上述源码中,第1行代码的@ConditionalOnClass({ILoadBalancer.class, Feign.class })注解中的ILoadBalancer.class是Ribbon依赖的类,只有引入该类,Ribbon和Feign的相关依赖引入后才会生效。

第3行代码的@AutoConfigureBefore(FeignAutoConfiguration.class)注解会在FeignAutoConfiguration生效前进行配置,因为FeignAutoConfiguration关联的到了Feign Client代码对象的实例化,而真正发请求调用的Client对象需要提前实例化。换句话说,如果Client对应Ribbon的实现类LoadBalancerFeignClient存在,就会调用Ribbon的负载均衡客户端处理请求,反之则使用默认的Defalut类。

第7-10行代码用于向容器注入负载均衡客户端LoadBalancerFeignClient,继续查看 LoadBalancerFeignClient源码,具体代码如下所示。

@Override
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);
       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);
    }
}

从上述代码可以看出,Spring Cloud Feign的负载均衡客户端功能是通过Spring Cloud Ribbon实现的,其中executeWithLoadBalancer()方法用于以负载均衡的方式发送请求。查看executeWithLoadBalancer()方法源码,具体代码如下所示。

public T executeWithLoadBalancer(final S request, 
        final IClientConfig requestConfig) throws ClientException {
        RequestSpecificRetryHandler handler = 
                 getRequestSpecificRetryHandler(request, requestConfig);
        LoadBalancerCommand<T> command = LoadBalancerCommand.<T>builder()
                .withLoadBalancerContext(this)
                .withRetryHandler(handler)
                .withLoadBalancerURI(request.getUri())
                .build();
        try {
            return command.submit(
                new ServerOperation<T>() {
                    @Override
                    public Observable<T> call(Server server) {
                        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);
            }
        }
    }

上述源码中,submit()方法隶属于LoadBalancerCommand类,该方法用于实现负载均衡。查看submit()方法源码,部分源码如下所示。

 1   Observable<T> o = 
 2                     (server == null ? selectServer() : Observable.just(server))
 3                     .concatMap(new Func1<Server, Observable<T>>() {
 4                         @Override
 5                         // Called for each server being selected
 6                         public Observable<T> call(Server server) {
 7                             context.setServer(server);
 8             }}

上述代码中,selectServer()方法最终会调用ILoadBalancer选择服务从而实现负载均衡。

点击此处
隐藏目录