Feign源码学习(6)— 找到feign和eureka整合位置

775 阅读1分钟

1. 找到ILoadBalancer的实现类

回到上一讲的代码位置:

位置在 SynchronousMethodHandler#invoke 方法上

  @Override
  public Object invoke(Object[] argv) throws Throwable {
    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;
      }
    }
  }

executeAndDecode(template) ,会调用 LoadBalancerFeignClient#execute 方法。

代码片段A:

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

这里可以重点关注一下, lbClient(clientName) 的方法,我们看下他做了什么?

	private FeignLoadBalancer lbClient(String clientName) {
   	return this.lbClientFactory.create(clientName);
   }

并且是一个CachingSpringLoadBalancerFactory是一个工厂类

private CachingSpringLoadBalancerFactory lbClientFactory;

再上 CachingSpringLoadBalancerFactory#create 的代码:

	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);
   	client = loadBalancedRetryFactory != null ? new RetryableFeignLoadBalancer(lb, config, serverIntrospector,
   		loadBalancedRetryFactory) : new FeignLoadBalancer(lb, config, serverIntrospector);
   	this.cache.put(clientName, client);
   	return client;
   }

代码怎么去分析呢?一些我就不看了,重点是关注 ILoadBalancer接口的实现 是谁就好了的。

其实很简单了,也是老规矩,找到 RibbonClientConfiguration 这种和spring boot 整合的代码。

	@Bean
   @ConditionalOnMissingBean
   public ILoadBalancer ribbonLoadBalancer(IClientConfig config,
   		ServerList<Server> serverList, ServerListFilter<Server> serverListFilter,
   		IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
   	if (this.propertiesFactory.isSet(ILoadBalancer.class, name)) {
   		return this.propertiesFactory.get(ILoadBalancer.class, config, name);
   	}
   	return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList,
   			serverListFilter, serverListUpdater);
   }

你看,ILoadBalancer默认的实现就是ZoneAwareLoadBalancer
PS: 如果是DEBUG的情况更加简单,直接通过idea 就能发现他的实现类是什么,比自己这么去找ribbon和boot 的整合简单多了。

2. ZoneAwareLoadBalancer负载均衡的流程

上述提到,ZoneAwareLoadBalancerILoadBalancer 的默认实现,那么,具体如何做负载均衡,还是要看下代码。

紧接着返回到上文的 代码片段A 上:
找到这个 executeWithLoadBalancer的方法:

这里代码就不贴了,因为涉及到RXJAVA的内容,比较复杂,也看不懂。

就理解成使用 LoadBalancerCommand 去进行调用把。

代码如下:

 public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
 		// 会有一个LoadBalancerCommand 来做这种逻辑
        LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig);

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