RestTemplate是Spring Web模块提供的一个基于Rest规范提供Http请求的工具。
Ribbon是springcloud中客户端负载均衡的组件。
我们在微服务架构中,往往通过RestTemplate发送RPC请求,然后通过Ribbon做客户端负载均衡。那么它们是如何配合工作的。
1、注册RestTemplate的Bean
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
2、@LoadBalanced
这个注解主要是通过拦截器在RestTemplate工作前进行负载均衡。该拦截器中注入了LoadBalancerClient(负载均衡客户端)的实现。当一个被@LoadBalanced注解修饰的RestTemplate对象向外发起HTTP请求时,会执行LoadBalancerInterceptor类的intercept函数。由于我们在使用RestTemplate时候采用了服务名作为host,所以直接从HttpRequest的URI对象中通过getHost()就可以拿到服务名,然后调用execute函数去根据服务名来选择实例并发起实际的请求。
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;
}
}
}
上述源码中LoadBalancerClient 是一个负载均衡的接口,有不同的负载均衡客户端实现,这里的实现是RibbonLoadBalancerClient
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 ex) {
statsRecorder.recordStats(ex);
throw ex;
}
catch (Exception ex) {
statsRecorder.recordStats(ex);
ReflectionUtils.rethrowRuntimeException(ex);
}
return null;
}
该客户端中通过工厂方法获取对应serviceId的负载均衡器,该负载均衡器接口的实现类是DynamicServerListLoadBalancer,通过负载均衡器获取请求的实例(server),然后进行访问。
3、负载均衡策略
事实上,DynamicServerListLoadBalancer是通过父类BaseLoadBalancer注入的负载均衡规则IRule接口的实现类完成负载均衡策略的。该接口的实现类是RoundRobinRule,它实现的策略是轮询规则。(这里是通过拉取server的列表,然后通过索引的原子性操作来完成轮询)
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
log.warn("no load balancer");
return null;
}
Server server = null;
int count = 0;
while (server == null && count++ < 10) {
List<Server> reachableServers = lb.getReachableServers();
List<Server> allServers = lb.getAllServers();
int upCount = reachableServers.size();
int serverCount = allServers.size();
if ((upCount == 0) || (serverCount == 0)) {
log.warn("No up servers available from load balancer: " + lb);
return null;
}
int nextServerIndex = incrementAndGetModulo(serverCount);
server = allServers.get(nextServerIndex);
if (server == null) {
/* Transient. */
Thread.yield();
continue;
}
if (server.isAlive() && (server.isReadyToServe())) {
return (server);
}
// Next.
server = null;
}
if (count >= 10) {
log.warn("No available alive servers after 10 tries from load balancer: "
+ lb);
}
return server;
}