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选择服务从而实现负载均衡。