本文已参与「新人创作礼」活动,一起开启掘金创作之路。
LoadBalancerInterceptor
@LoadBalanced
spring.factories
LoadBalancerAutoConfiguration
LoadBalancerInterceptor & ClientHttpRequestInterceptor
LoadBalancerClient & RibbonLoadBalancedClient
LoadBalancerRequest & LoadBalancerRequestFactory
LocalBalancerClient
spring.factories
RibbonAutoConfiguration
SpringClientFactory
RibbonLoadBalancerClient & LoadBalancerClient & LoadBalancerChooser
LocalBalancer
SpringClientFactory & NamedContextFactory
RibbonClientConfiguration
IConfig & IRule & IPing & ServerList & ServerListUpdater & ServerListFilter
ZoneAwareLoadBalancer & DynamicServerListLoadBalancer & BaseLoadBalancer & ILoadBalancer
ServerList
EurekaRibbonConfiguration
DomainExtractingServerList(DiscoveryEnableNIWSServerList) & ServerList
ServerListUpdater
PollingServerListUpdater
IRule
ZoneAvoidanceRule & PredicateBasedRule & ClientConfigEnableRoundRibbonRule & AbstractLoadBalancerRule & IRule
Netflix Ribbon - 发送真正的网络请求
- Interceptor
LoadBalancerInterceptor拦截器拦截RestTemplate请求,LoadBalancerInterceptor.intercept()方法调用RibbonLoadBalancerClient.execute()方法将RestTemplate请求交由LoadBalancerClient处理。
- LoadBalancerClient execute & choose
RibbonLoadBalancerClient.execute()方法根据serviceId获取List,RibbonLoadBalancerClient.choose()方法选择一个Server,封装成RibbonServer。
- LoadBalancerRequest
RibbonLoadBalancerRequest通过装饰器将HttpRequest封装成ServiceRequestWrapper。
ServiceRequestWrapper通过RibbonLoadBalancerClient.reconstructURI()获取真正请求的HTTP地址。
- ClientHttpRequestFactory
获取到真正的HTTP地址之后通过ClientHttpRequestFactory.createRequest(uri, method)构造出ClientHttpRequest发起真正的网络请求。
LoadBalancerRequest
在LoadBalancerInterceptor中,调用RibbonLoadBalancerClient.execute()方法需要两个参数:
serviceId:用来获取List并选择一个Server。
ClientHttpRequest:Http请求报文客户端(发起Http请求的客户端,并不是RestTemplate、LoadBalancerClient之类的模版方法客户端。RestTemplate自身不实现http申请,http申请的底层实现是ClientHttpRequest框架)
// LoadBalancerRequestFactory.createRequest
public LoadBalancerRequest<ClientHttpResponse> createRequest(
final HttpRequest request, final byte[] body,
final ClientHttpRequestExecution execution) {
return instance -> {
// 装饰器模式。getURI()方法被重载,由LoadBalancerClient.reconstructURI处理。
HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance,
this.loadBalancer);
if (this.transformers != null) {
for (LoadBalancerRequestTransformer transformer : this.transformers) {
serviceRequest = transformer.transformRequest(serviceRequest,
instance);
}
}
return execution.execute(serviceRequest, body);
};
}
LoadBalancerRequest,是一个匿名内部类的实例。
// RibbonLoadBalancerClient.execute
@Override
public <T> T execute(String serviceId, LoadBalancerRequest<T> request)
throws IOException {
return execute(serviceId, request, null);
}
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
throws IOException {
// 根据serviceId获取ILoadBalancer
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
// 根据ILoadBalancer选择Server
Server server = getServer(loadBalancer, hint);
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
}
// 封装Server为RibbonServer
RibbonServer ribbonServer = new RibbonServer(serviceId, server,
isSecure(server, serviceId),
serverIntrospector(serviceId).getMetadata(server));
//
return execute(serviceId, ribbonServer, request);
}
@Override
public <T> T execute(String serviceId, ServiceInstance serviceInstance,
LoadBalancerRequest<T> request) throws IOException {
Server server = null;
if (serviceInstance instanceof RibbonServer) {
server = ((RibbonServer) serviceInstance).getServer();
}
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
}
RibbonLoadBalancerContext context = this.clientFactory
.getLoadBalancerContext(serviceId);
RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);
try {
// LoadBalancerRequest发送请求到ServiceInstance
T returnVal = request.apply(serviceInstance);
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;
}
RibbonLoadBalancerClient.execute()方法调用LoadBalancerRequest.apply()方法,传入选择出来的server,对server发起指定的一个请求(LoadBalancerRequest)
http://ServiceA/product ,这个URL地址封装在LoadBalancerRequest中。
通过装饰器模式将HttpRequest和ServiceInstance(RibbonServer)封装进ServerRequestWrapper中。
将ServiceRequestWrappter交由ClientHttpRequestExecution生成ClientHttpRequest,并发起HTTP请求。
从ServiceRequestWrapper中获取出来对应的真正的请求URL地址,然后发起了一次请求
Spring-Web的ClientHttpRequestExecution是从ServiceRequestWrapper.getURI()中,获取对应重构之后的真正的请求URL地址。
// ServiceRequestWrapper.getURI
@Override
public URI getURI() {
URI uri = this.loadBalancer.reconstructURI(this.instance, getRequest().getURI());
return uri;
}
ServiceRequestWrapper.getURI()方法基于自己的逻辑重写,调用了RibbonLoadBalancerClient.reconstructURI()方法,基于选择出来的server的地址,重构请求URI。
传进去负载均衡算法选择出来的server,getRequest().getURI() 根据LoadBalancerClient.reconstructURI()重构URI返回新的RUI(http://ServiceA/product => http://192.168.1.2:7071/product) ,将ServiceA给替换成了实际选择出来的server对应的hostname:port
用http://192.168.1.2:7071/product,基于底层的ClientHttpRequestExecution(有拦截器走拦截器,没有拦截器就根据request.getURI和request.getMethod调用ClientHttpRequestFactory.createRequest构造ClientHttpRequest)发起一次http请求。