feign负载均衡实际是指ribbon的负载均衡,本文主要围绕feign如何在代码中调用ribbon流程进行解析。
上篇分析feign的加载流程以及如何执行,但是关于客户端如何执行还留有疑问。
这里以业务中常用的okHttp为例,
ConditionalOnClass({ ILoadBalancer.class, Feign.class })
@Configuration
@AutoConfigureBefore(FeignAutoConfiguration.class)
@EnableConfigurationProperties({ FeignHttpClientProperties.class })
@Import({ HttpClientFeignLoadBalancedConfiguration.class,
OkHttpFeignLoadBalancedConfiguration.class,
DefaultFeignLoadBalancedConfiguration.class })
public class FeignRibbonClientAutoConfiguration {
}
这里注意到FeignRibbonClientAutoConfiguration会比FeignAutoConfiguration提前加载,因为使用了okHttp,进入OkHttpFeignLoadBalancedConfiguration。
@Configuration
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnProperty("feign.okhttp.enabled")
class OkHttpFeignLoadBalancedConfiguration {
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory, okhttp3.OkHttpClient okHttpClient) {
OkHttpClient delegate = new OkHttpClient(okHttpClient);
return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
}
}
feignClient会包装原生的OkHttpClient,最后组装LoadBalancerFeignClient的对象,用于执行http请求。
调用LoadBalancerFeignClient的execute,省略掉参数配置,关键就如下一行代码。
public Response execute(Request request, Request.Options options) throws IOException {
...
return lbClient(clientName)
.executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
...
}
1. 获取负载均衡客户端
private FeignLoadBalancer lbClient(String clientName) {
// lbClientFactory是指CachingSpringLoadBalancerFactory
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);
// 这里默认情况下是创建FeignLoadBalancer
client = this.loadBalancedRetryFactory != null
? new RetryableFeignLoadBalancer(lb, config, serverIntrospector,
this.loadBalancedRetryFactory)
: new FeignLoadBalancer(lb, config, serverIntrospector);
// 放入缓存
this.cache.put(clientName, client);
return client;
}
无论生成那种FeignLoadBalancer,这里都不影响负载均衡的使用。
2.executeWithLoadBalancer
executeWithLoadBalancer 主要逻辑分为两步:
- 根据负载策略获取对应的服务
- 向对应服务执行发起请求
public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig);
try {
// submit包含负载均衡的处理,内部执行的任务是具体的请求逻辑
return command.submit(...);
...
}
public Observable<T> submit(final ServerOperation<T> operation) {
// selectServer这里调用处理负载
Observable<T> o = (server == null ? selectServer() : Observable.just(server))
....
}
private Observable<Server> selectServer() {
return Observable.create(new OnSubscribe<Server>() {
@Override
public void call(Subscriber<? super Server> next) {
try {
Server server = loadBalancerContext.getServerFromLoadBalancer(loadBalancerURI, loadBalancerKey);
next.onNext(server);
next.onCompleted();
}
...
}
});
}
public Server getServerFromLoadBalancer(@Nullable URI original, @Nullable Object loadBalancerKey) throws ClientException {
...
ILoadBalancer lb = getLoadBalancer();
if (host == null) {
if (lb != null){
// 执行具体的均衡策略,默认为轮询
Server svc = lb.chooseServer(loadBalancerKey);
...
}
...
}
...
}