一个用于客户端的负载均衡工具
@Configuration
public class RibbonConfig{
@LoadBalanced
public RestTemplate restTemplate(){
// ...
}
}
只需要在配置类中对RestTemplate
的Bean加上@LoadBalanced
注解即可。之后在使用RestTemplate
进行请求调用时,会自动实现负载均衡
public class RestTemplate extends InterceptingHttpAccessor implements RestOperations
RestTemplate
继承了InterceptingHttpAccessor
类,其内部持有一系列请求拦截器List
,这些拦截器可以对ClientHttpRequest
和ClientHttpResponse
进行拦截修改。
RestTemplate
在发出请求时,会首先创建一个ClientHttpRequest
,如下(删掉了一些代码)
protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor) throws RestClientException {
//创建请求
ClientHttpRequest request = createRequest(url, method);
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
response = request.execute();
handleResponse(url, method, response);
if (responseExtractor != null) {
return responseExtractor.extractData(response);
}
}
createRequest()
是使用工厂模式进行创建
protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
ClientHttpRequest request = getRequestFactory().createRequest(url, method);
return request;
}
而RestTemplate
本身又提供了该工厂的实现
public ClientHttpRequestFactory getRequestFactory() {
ClientHttpRequestFactory delegate = super.getRequestFactory();
if (!CollectionUtils.isEmpty(getInterceptors())) {
return new InterceptingClientHttpRequestFactory(delegate, getInterceptors());
}
else {
return delegate;
}
}
这个工厂创建的Request
会先走一遍拦截器
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());
for (Map.Entry<String, List<String>> entry : request.getHeaders().entrySet()) {
List<String> values = entry.getValue();
for (String value : values) {
delegate.getHeaders().add(entry.getKey(), value);
}
}
if (body.length > 0) {
StreamUtils.copy(body, delegate.getBody());
}
return delegate.execute();
}
}
Ribbon
正是利用了这个拦截器的扩展,为RestTemplate
添加了一个拦截器——负载均衡客户端,在创建请求时,会通过负载均衡拿到合适的服务实例地址,再交给RestTemplate
请求
在LoadBalancerAutoConfiguration
中,Ribbon创建了一个负载均衡拦截器LoadBalancerInterceptor
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor
并创建工具类restTemplateCustomizer
将LoadBalancerInterceptor
添加到了RestTemplate
中。
RestTemplate
在请求第一步创建Request时会进入到LoadBalancerInterceptor
中的intercept()
方法
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
final ClientHttpRequestExecution execution) throws IOException {
final URI originalUri = request.getURI();
String serviceName = originalUri.getHost();
Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution));
}
这个方法委托给负载均衡客户端LoadBalancerClient
来执行,即RibbonLoadBalancerClient
的execute()
方法
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));
return execute(serviceId, ribbonServer, request);
}
protected Server getServer(ILoadBalancer loadBalancer) {
if (loadBalancer == null) {
return null;
}
return loadBalancer.chooseServer("default"); // TODO: better handling of key
}
其负载均衡选择服务的操作最终通过ILoadBalancer
接口提供了chooseServer
来进行选择,默认提供了三种实现
NoOpLoadBalancer:不使用负载均衡
DynamicServerListLoadBalancer:维持了一份动态的服务列表,并提供服务的过滤来筛选服务
ZoneAwareLoadBalancer:DynamicServerListLoadBalancer的子类,提供区域过滤,集成Eureka时的默认策略
ILoadBalancer
使用策略接口IRule
来决定负载均衡选择的具体规则。
用于监测服务是否可用,在与注册中心结合时会使用NoOpPing
策略,取消监测,交由注册中心来实现。