Ribbon源码分析之地址转换

1,045 阅读3分钟

本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。

Ribbon源码分析之地址转换

RibbonLoadBalancerClient的execute方法:

@Override
public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
   ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
   Server server = getServer(loadBalancer);
   if (server == null) {
      throw new IllegalStateException("No instances available for " + serviceId);
   }
   RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server,
         serviceId), serverIntrospector(serviceId).getMetadata(server));

   RibbonLoadBalancerContext context = this.clientFactory
         .getLoadBalancerContext(serviceId);
   RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);

   try {
      T returnVal = request.apply(ribbonServer);
      statsRecorder.recordStats(returnVal);
      return returnVal;
   }
   // catch IOException and rethrow so RestTemplate behaves correctly
   catch (IOException ex) {
      statsRecorder.recordStats(ex);
      throw ex;
   }
   catch (Exception ex) {
      statsRecorder.recordStats(ex);
      ReflectionUtils.rethrowRuntimeException(ex);
   }
   return null;
}

protected Server getServer(ILoadBalancer loadBalancer) {
		if (loadBalancer == null) {
			return null;
		}
		return loadBalancer.chooseServer("default"); // TODO: better handling of key
	}java
    

RibbonLoadBalancerClient的execute方法中,通过ZoneAwareLoadBalancer的chooseServer方法获取到负载均衡策略分配到的服务实例对象Server之后,将其内容包装成RibbonServer对象,然后实行该对象回调LoadBalancerInterceptor请求拦截器中LoadBalancerRequest的apply方法,向一个实际的具体服务实例发起请求,从而实现一开始以服务名为host的URI请求到host:port形式的实际访问地址的转换。

RibbonLoadBalancerClient的RibbonStatsRecorder是用来对服务的请求进行跟踪记录

apply的具体实现是在LoadBalancerInterceptor中

public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {

	private LoadBalancerClient loadBalancer;

	public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
		this.loadBalancer = loadBalancer;
	}

	@Override
	public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
			final ClientHttpRequestExecution execution) throws IOException {
		final URI originalUri = request.getURI();
		String serviceName = originalUri.getHost();
		return this.loadBalancer.execute(serviceName,
				new LoadBalancerRequest<ClientHttpResponse>() {

					@Override
					public ClientHttpResponse apply(final ServiceInstance instance)
							throws Exception {
						HttpRequest serviceRequest = new ServiceRequestWrapper(request,
								instance);
						return execution.execute(serviceRequest, body);
					}

				});
	}

	private class ServiceRequestWrapper extends HttpRequestWrapper {

		private final ServiceInstance instance;

		public ServiceRequestWrapper(HttpRequest request, ServiceInstance instance) {
			super(request);
			this.instance = instance;
		}

		@Override
		public URI getURI() {
			URI uri = LoadBalancerInterceptor.this.loadBalancer.reconstructURI(
					this.instance, getRequest().getURI());
			return uri;
		}

	}

}

ServiceRequestWrapper继承HttpRequestWrapper,重写getURI方法,重写后的getURI方法通过LoadBalancerClient接口的reconstructURI方法来重写构建URI来访问

然后执行execution.execute(serviceRequest, body);

调用InterceptingClientHttpRequest子类InterceptingRequestExecution的execute方法:

private class InterceptingRequestExecution implements ClientHttpRequestExecution {

   private final Iterator<ClientHttpRequestInterceptor> iterator;

   public InterceptingRequestExecution() {
      this.iterator = interceptors.iterator();
   }

   @Override
   public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
      if (this.iterator.hasNext()) {
         ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
         return nextInterceptor.intercept(request, body, this);
      }
      else {
         ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), request.getMethod());
         delegate.getHeaders().putAll(request.getHeaders());
         if (body.length > 0) {
            StreamUtils.copy(body, delegate.getBody());
         }
         return delegate.execute();
      }
   }
}

requestFactory.createRequest(request.getURI(), request.getMethod()); 中request.getURI(),会调用之前介绍的ServiceRequestWrapper的getURI方法,调用RibbonLoadBalancerClient的reconstructURI方法构建URI:

@Override
public URI reconstructURI(ServiceInstance instance, URI original) {
   Assert.notNull(instance, "instance can not be null");
   String serviceId = instance.getServiceId();
   RibbonLoadBalancerContext context = this.clientFactory
         .getLoadBalancerContext(serviceId);
   Server server = new Server(instance.getHost(), instance.getPort());
   boolean secure = isSecure(server, serviceId);
   URI uri = original;
   if (secure) {
      uri = UriComponentsBuilder.fromUri(uri).scheme("https").build().toUri();
   }
   return context.reconstructURIWithServer(server, uri);
}
  • SpringClientFactory是用来创建客户端负载均衡器的工厂类,该工厂类会为每一个不同命的Ribbon客户端生成不同的Spring上下文
  • RibbonLoadBalancerContext是LoadBalancerContext的子类,该类用于存储一些被负载均衡器使用的上下文内容和API操作

使用ServiceInstance的host port信息构建Server对象调用reconstructURIWithServer方法:

LoadBalancerContext的reconstructURIWithServer:

public URI reconstructURIWithServer(Server server, URI original) {
    String host = server.getHost();
    int port = server .getPort();
    if (host.equals(original.getHost()) 
            && port == original.getPort()) {
        return original;
    }
    String scheme = original.getScheme();
    if (scheme == null) {
        scheme = deriveSchemeAndPortFromPartialUri(original).first();
    }

    try {
        StringBuilder sb = new StringBuilder();
        sb.append(scheme).append("://");
        if (!Strings.isNullOrEmpty(original.getRawUserInfo())) {
            sb.append(original.getRawUserInfo()).append("@");
        }
        sb.append(host);
        if (port >= 0) {
            sb.append(":").append(port);
        }
        sb.append(original.getRawPath());
        if (!Strings.isNullOrEmpty(original.getRawQuery())) {
            sb.append("?").append(original.getRawQuery());
        }
        if (!Strings.isNullOrEmpty(original.getRawFragment())) {
            sb.append("#").append(original.getRawFragment());
        }
        URI newURI = new URI(sb.toString());
        return newURI;            
    } catch (URISyntaxException e) {
        throw new RuntimeException(e);
    }
}

从Server对象中获取host和port信息,根据服务名为host的URI对象original获取其他请求信息,将两者内容拼接整合,形成最终要访问的具体地址

总结:通过LoadBalancerInterceptor拦截器对RestTemplate的请求进行拦截,利用SpringCloud的负载均衡器LoadBalancerClient将以逻辑服务名为host的URI转换成具体的服务实例地址。