本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。
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转换成具体的服务实例地址。