Ribbon——A ribbon is a long, narrow piece of cloth that you use for tying things together or as a decoration.
Ribbon是一个工具类框架,不需要独立部署。
负载均衡设备/负载均衡软件模块都会维护一个可用的服务清单,通过心跳检测来剔除故障节点,保证清单中都是可用节点。
客户端负载均衡,由客户端节点维护要访问的服务清单,服务清单来自于注册中心。
如前所示,使用客户端负载均衡调用分两步:
1. 服务提供者注册到服务中心。
2. 服务消费者通过标有@LoadBalanced
注解的RestTemplate
进行服务调用。
在service-consumer
服务中,通过调用RestTemplate
的getForEntity
方法,GET调用hello-service
的/hello
接口。
RestTemplate
GET
RestTemplate
有两类GET
实现:getForEntity
和getForObject
。
getForEntity()
有三个重载实现,均返回ResponseEntity
,
// url为请求地址,responseType为响应体body的类型,uriVariables为url参数
// uriVariables配合url中的占位符进行动态传参,如:
// entity = getForEntity("http://user-serivce/user?name={1}", String.class, "John");,将John传给参数name
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); // new AcceptHeaderRequestCallback(responseType)
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); // new ResponseEntityResponseExtractor<>(responseType)
return nonNull(execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables));
}
// uriVariables为Map类型,key需要与url中的占位符对应,如:
// params.put("myname", "John");
// entity = getForEntity("http://user-service/user?name={myname}", String.class, params); 将key为myname对应的value——John传给name
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables);
// 使用URI对象代替url和uriVariables
public <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType);
// 使用:
ResponseEntity<String> entity = restTemplate.getForEntity(url, String.class, "John");
String body = entity.getBody();
ResponseEntity<User> entity = restTemplate.getForEntity(url, User.class, "John");
User body = entity.getBody();
getForObject()
也有三个重载实现,传入execute方法的不是ResponseExtractor,而是HttpMessageConverterExtractor,返回的则是对象类型,三个重载和getForEntity
的三个重载关系类似:
public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); // new AcceptHeaderRequestCallback(responseType)
HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
}
public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables);
public <T> T getForObject(URI url, Class<T> responseType);
// 使用:
String result = restTemplate.getForObject(url, String.class, "John");
User user = restTempleate.getForObject(url, User.class, "John");
// 使用getForObject可以省略从response中获取body的步骤
POST
RestTemplate
有三类POST
实现:postForEntity
和postForObject
,postForLocation
。
postForEntity()
有三个重载实现,均返回ResponseEntity
,
// 相较于getForEntity,新增参数Object request,reqeust如果是HttpEntity对象,RestTemplate将其当作完整的http请求对象处理,request中包含了header和body的内容。如果request是普通对象,RestTemplate将其转换为HttpEntity来处理,request作为body。
// if (request instanceof HttpEntity){this.requestEntity = (HttpEntity<?>) request; }
// else if (requestBody != null) { this.requestEntity = new HttpEntity<>(request); }
// else { this.requestEntity = HttpEntity.EMPTY; }
// 使用:
// User user = new User("didi", 30);
// entity = postForEntity("http://user-serivce/user", user, String.class, "John");,将John传给参数name
public <T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables) throws RestClientException {
RequestCallback requestCallback = httpEntityCallback(request, responseType); // new HttpEntityRequestCallback(requestBody, responseType)
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); // new ResponseEntityResonseExtracor<>(responseType)
return nonNull(execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables));
}
public <T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables);
public <T> ResponseEntity<T> postForEntity(URI url, Object request, Class<T> responseType);
postForObject()
也有三个重载实现,传入execute方法的不是ResponseExtractor,而是HttpMessageConverterExtractor,返回的则是对象类型,三个重载和postForEntity
的三个重载关系类似:
public <T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables) throws RestClientException {
RequestCallback requestCallback = httpEntityCallback(request, responseType);
HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
}
public <T> T postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables);
public <T> T postForObject(URI url, Object request, Class<T> responseType);
postForLocation()
用于发送post请求,返回新资源的URI,有三个重载实现,均返回URI对象,
public URI postForLocation(String url, Object request, Object... uriVariables) throws RestClientException {
RequestCallback requestCallback = httpEntityCallback(request); // new HttpEntityRequestCallback(request, null)
HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, headersExtractor(), uriVariables); // new HeadersExtractor()
return (headers != null ? headers.getLocation() : null);
}
public URI postForLocation(String url, Object request, Map<String, ?> uriVariables);
public URI postForLocation(URI url, Object ruquest);
execute
RestTemplate
中,不同的请求方式,最终会调用到execute
的三个重载实现上来
// ------------------------------------
public <T> T execute(String url, HttpMethod method, RequestCallback requestCallback, ResponseExtractor<T> responseExtractor, Object... uriVariables) {
URI expanded = getUriTemplateHandler().expand(url, uriVariables);
return doExecute(expanded, method, requestCallback, responseExtractor);
}
public <T> T execute(String url, HttpMethod method, RequestCallback requestCallback, ResponseExtractor<T> responseExtractor, Map<String, ?> uriVariables);
public <T> T execute(URI url, HttpMethod method, RequestCallback requestCallback, ResponseExtractor<T> responseExtractor);
execute()
的三个重载实现,都会调用doExecute()
方法,去执行请求
protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback, ResponseExtractor<T> responseExtractor) throws RestClientException {
ClientHttpResponse response = null;
ClientHttpRequest request = createRequest(url, method);
if (requestCallback != null) { requestCallback.doWithRequest(request); }
response = request.execute();// 此处执行前会被拦截
handleResponse(url, method, response);
return (responseExtractor != null ? responseExtractor.extractData(response) : null);
}
doExecute()
方法接收的参数中,有RequestCallback和ResponseExtractor。
RequestCallback
AcceptHeaderRequestCallback implements RequestCallback // AcceptHeaderRequestCallback用于GET请求
HttpEntityRequestCallback extends AcceptHeaderRequestCallback // HttpEntityRequestCallback 用于POST、PUT等请求
ResponseEntity
// ResponseEntity扩展自HttpEntity,增加了http的status(http请求状态码)
package org.springframework.http;
public class ResponseEntity<T> extends HttpEntity<T> {
private final Object status; // status为int或HttpStatus类型
// getter/setter...
}
// HttpEntity表示http的request或response的entity,包含headers(http请求的头信息)和body(http请求的请求体)
package org.springframework.http;
public class HttpEntity<T> {
private final HttpHeaders headers;
private final T body;
// getter/setter...
}
@LoadBalanced
在服务消费者中,给RestTemplate
添加了@LoadBalanced
注解,根据注释,该注解用于标记RestTemplate
使用LoadBalancerClient
来配置,即客户端负载均衡器。
// Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient.
public @interface LoadBalanced {}
LoadBalancerClient
客户端负载均衡器,具有如下能力:
// Represents a client-side load balancer. 即客户端负载均衡器
interface LoadBalancerClient extends ServiceInstanceChooser {
// 使用serviceId服务执行request请求
<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;
<T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;
// 将逻辑服务名http://myservice/path/to/service 替换为host:port的形式
URI reconstructURI(ServiceInstance instance, URI original);
}
// 选择一个server用来发送请求的实现接口
interface ServiceInstanceChooser {
// 根据serviceId,从负载均衡器选择一个服务实例ServiceInstance
ServiceInstance choose(String serviceId);
}
LoadBalancerClient
有一个实现类RibbonLoadBalancerClient
。
在RestTemplate
的doExecute()
方法中,调用request.execute()
之前,会被LoadBalancerInterceptor
拦截。该拦截器中有一个LoadBalancerClient
实例,此外该拦截器在LoadBalancerAutoConfiguration
中被创建。而LoadBalancerAutoConfiguration
有两个特殊的注解@ConditionalOnClass(RestTemplate.class)
和@ConditionalOnBean(LoadBalancerClient.class)
,且注释明确说明LoadBalancerAutoConfiguration
为Ribbon
的自动化配置类。
spring-cloud-commons的loadbalancer包中的配置类,以2.1.2为例
LoadBalancerAutoConfiguration
:创建LoadBalancerInterceptor、创建RestTemplateCustomizer(匿名内部类)、创建LoadBalancerRequestFactory、创建SmartInitializingSingleton(匿名内部类)AsyncLoadBalancerAutoConfiguration
:针对AsyncRestTemplate做的类似配置
spring-cloud-netflix-ribbon中的几个配置类,以2.1.2为例
RibbonClientConfiguration
:创建IClientConfig、创建IRule,创建IPing,创建ServerList<Server>,创建ServerListUpdater,创建ILoadBalancer(使用ZoneAwareLoadBalancer实现),创建ServerListFilter、创建RibbonLoadBalancerContext,创建RetryHandler,创建ServerIntrospectorRibbonAutoConfiguration
:创建HasFeatures,创建SpringClientFactory,创建LoadBalancerClient(使用RibbonLoadBalancerClient实现),创建LoadBalancedRetryFactory(使用RibbonLoadBalancedRetryFactory实现),创建PropertiesFactory,创建RibbonApplicationContextInitializer,创建RestTemplateCustomizer(使用匿名内部类),创建RibbonClientHttpRequestFactoryRestCilentRibbonConfiguration
:创建RestClient
LoadBalancerAutoConfiguration
Ribbon
的自动化配置类:
@Configuration
@ConditionalOnClass(RestTemplate.class) // 需要RestTemplate类在classpath中
@ConditionalOnBean(LoadBalancerClient.class) // 需要LoadBalancerClient的实现Bean在BeanFactory中
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
class LoadBalancerAutoConfiguration {
@LoadBalanced
@Autowired(required = false)
// 工程中注册的RestTemplate的Bean会在此被加载
private List<RestTemplate> restTemplates = Collections.emptyList();
@Autowired(required = false)
private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();
// 创建SmartInitializingSingleton的Bean,负责用每个Customizer去修饰每个RestTemplate
@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
return () -> restTemplateCustomizers.ifAvailable(customizers -> {
for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
for (RestTemplateCustomizer customizer : customizers) {
customizer.customize(restTemplate);
}
}
});
}
@Configuration
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
static class LoadBalancerInterceptorConfig {
// 创建拦截器Bean,入参为客户端负载均衡器
@Bean
public LoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
// 创建一个RestTemplateCustomizer的Bean,负责将负载均衡拦截器加到入参RestTemplate的拦截器列表中,添加方式为get、add、set
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {
return restTemplate -> {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
}
}
}
}
LoadBalancerInterceptor
负载均衡拦截器,用于在请求最终执行前进行拦截,在拦截器的intercept()
方法中,首先从request中获取服务名称serviceName,然后调用request工厂的createRequest()
方法,创建一个负载均衡的request——LoadBalancerRequest实例,最后将其连同serviceName一起作为LoadBalancerClient
的execute()
方法的入参。
class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
LoadBalancerClient loadBalancer;
LoadBalancerRequestFactory requestFactory;
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) {
// 此处调用request的getURI方法,
final URI originalUri = request.getURI();
String serviceName = originalUri.getHost();
return this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
}
}
LoadBalancerRequestFactory——创建LoadBalancerRequest的工厂
public LoadBalancerRequest<ClientHttpResponse> createRequest(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) {
// 返回LoadBalancerRequest的匿名内部类
return instance -> {
// 该LoadBalancerRequest的匿名内部类实现,先创建一个ServiceRequestWrapper的request,然后调用execution的execute方法执行请求
HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance, this.loadBalancer);
return execution.execute(serviceRequest, body);
};
}
LoadBalancerRequest
LoadBalancerRequest
使用该接口的apply()
的方法,为request添加处理动作
interface LoadBalancerRequest<T> {
T apply(ServiceInstance instance);
}
ServiceRequestWrapper
ServiceRequestWrapper
继承自HttpRequestWrapper
,HttpRequestWrapper
对外提供了获取一个request的method
、URI
、headers
、methodValue
等信息的方法。
ServiceRequestWrapper
改写了默认的getURI()
方法,使用客户端负载均衡器LoadBalancerClient
的重构URI的方法,将入参request的URI进行重构,其具体实现在LoadBalancerClient
的实现类RibbonLoadBalancerClient
中。
class ServiceRequestWrapper extends HttpRequestWrapper {
private final ServiceInstance instance;
private final LoadBalancerClient loadBalancer;
public ServiceRequestWrapper(HttpRequest request, ServiceInstance instance, LoadBalancerClient loadBalancer) {}
@Override
public URI getURI() {
// 调用loadBalancer的reconstructURI方法,进行URI重构,改写成host:port的形式,具体实现在RibbonLoadBalancerClient中
return this.loadBalancer.reconstructURI(this.instance, getRequest().getURI());
}
}
RibbonLoadBalancerClient
RibbonLoadBalancerClient
实现了LoadBalancerClient
和ServiceInstanceChooser
中的execute
、reconstructURI
、choose
方法,完成了请求执行、URI重构和选择服务实例的任务,execute
class RibbonLoadBalancerClient implements LoadBalancerClient {
SpringClientFactory clientFactory;
@Override
public URI reconstructURI(ServiceInstance instance, URI original) {
String serviceId = instance.getServiceId();
RibbonLoadBalancerContext context = this.clientFactory.getLoadBalancerContext(serviceId);
URI uri; Server server;
if (instance instanceof RibbonServer) {
server = ((RibbonServer) instance).getServer();
uri = updateToSecureConnectionIfNeeded(original, ribbonServer);
} else {
server = new Server(instance.getScheme(), instance.getHost(), instance.getPort());
IClientConfig clientConfig = clientFactory.getClientConfig(serviceId);
ServerIntrospector serverIntrospector = serverIntrospector(serviceId);
uri = updateToSecureConnectionIfNeeded(original, clientConfig, serverIntrospector, server);
}
// 用server中的host、port等替换原始uri
return context.reconstructURIWithServer(server, uri);
}
public ServiceInstance choose(String serviceId, Object hint) {
// 先调用getLoadBalancer方法,根据serviceId,获取一个ILoadBalancer
// 然后调用getServer(ILoadBalancer loadBalancer, Object hint),使用loadBalancer选择一个Server,hint默认为"default"
Server server = getServer(getLoadBalancer(serviceId), hint);
if (server == null) { return null; }
// 用入参serviceId、选择的Server,构造一个RibbonServer
return new RibbonServer(serviceId, server, isSecure(server, serviceId), serverIntrospector(serviceId).getMetadata(server));
}
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) {
// 首先选择一个RibbonServer,该部分流程与choose相同
Server server = getServer(getLoadBalancer(serviceId), hint);
RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server, serviceId), serverIntrospector(serviceId).getMetadata(server));
// 调用execute的重载实现,执行请求
return execute(serviceId, ribbonServer, request);
}
public <T> T execute(String serviceId, ServiceInstance serviceIntance, LoadBalancerRequest<T> request) {
Server server = null;
if (serviceInstance instanceof RibbonServer) {
server = ((RibbonServer) serviceInstance).getServer();
}
RibbonLoadBalancerContext context = this.clientFactory.getLoadBalancerContext(serviceId);
RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);
try {
// 调用apply,向服务实例发起请求
T returnVal = request.apply(serviceInstance);
statsRecorder.recordStats(returnVal);
return returnVal;
} catch (IOException ex) {
statsRecorder.recordStats(ex); throw ex;
} catch (Exception ex) {
statsRecorder.recordStats(ex); throw ex;
}
return null;
}
// Ribbon 实现了ServiceInstance接口,即服务实例接口
public static class RibbonServer implements ServiceInstance {
private final String serviceId;
private final Server server;
private final boolean secure;
private Map<String, String> metadata;
// @Override方法
}
}
ILoadBalancer
在RibbonLoadBalancerClient
的choose()
和execute()
方法中,都是通过调用ILoadBalancer
的chooseServer()
方法,来选择一个服务实例Server的,该ILoadBalancer
接口是由Ribbon定义的。
在ILoadBalancer
接口中,定义了软件负载均衡器的操作:一个服务实例的集合、标记一个服务停止、选择服务
package com.netflix.loadbalancer;
interface ILoadBalancer {
void addServers(List<Server> newServers); // 初始化、后续添加服务列表
Server chooseServer(Object key); // 从负载均衡器选择一个服务实例
void markServerDown(Server server); // 标记并通知某个服务实例已经停止
List<Server> getReachableServers(); // up/reachable状态的服务实例,可以提供正常服务
List<Server> getAllServers(); // 所有已知的服务实例,reachable/unreachable都包括
}
其中的Server
,代表一个服务端节点,包含了一个服务的基本信息:host、port、scheme、id、zone、元数据等等。
在RibbonLoadBalancerClient
的choose()
和execute()
方法中,通过getLoadBalancer()
方法,来根据serviceId
获取ILoadBalancer
的实例,然后将其包装成RibbonServer
。
配置类RibbonClientConfiguration
创建ILoadBalancer
时如果配置文件里有配置,则使用配置的实现,否则默认使用ZoneAwareLoadBalancer
实现。
ClientHttpRequestExecution
RibbonLoadBalancerClient
的execute()
方法中,调用了入参LoadBalancerRequest
的apply方法,execute()
方法在LoadBlancerInterceptor
的intercept
方法中调用,并传入LoadBalancerRequestFactory.createRequest
创建的LoadBalancerRequest
实现,其实现中最终使用ClientHttpRequestExecution
的execute
方法执行请求。
总结
- 使用时,注册一个使用
@LoadBalanced
注解修饰的RestTemplate
,在需要发起请求的地方调用RestTemplate
的相应的请求方法,最终调用到其doExecute
方法。 @LoadBalanced
注解关联了LoadBalancerClient
。- 配置类
LoadBalancerAutoConfiguration
注册了如下Bean:SmartInitializingSingleton
:遍历restTemplates、遍历customizers,并customizer.customize(restTemplate)
LoadBalancerRequestFactory
:使用LoadBalancerClient
构造LoadBalancerInterceptor
:使用LoadBalancerClient
和LoadBalancerRequestFactory
创建RestTemplateCustomizer
:使用LoadBalancerInterceptor
构造一个匿名类,将注册的LoadBalancerInterceptor
添加进restTemplate
的interceptors
列表中
- 拦截器
LoadBalancerInterceptor
的intercept
方法从原始请求中获取URI
,然后使用LoadBalancerClient
的execute
方法执行请求,接收两个参数:serviceName即host和请求工厂创建的request - 请求工厂
LoadBalancerRequestFactory
的createRequest
方法,由原始请求创建一个LoadBalancerRequest
的匿名实现 - 负载均衡请求
LoadBalancerRequest
接口只有apply
方法,其匿名实现创建HttpRequest
的实现类ServcieRequestWrapper
的实例,然后由ClientHttpRequestExecution
的execute
方法执行请求,返回响应ClientHttpResponse
ServiceRequestWrapper
重写了父类HttpRequestWrapper
的getURI
方法,返回LoadBalancerClient
的reconstructURI
方法重构的URIClientHttpRequestExecution
的实现类是InterceptingClientHttpRequest
的内部类IntercpetingRequestExecution
,其execute
方法遍历interceptors
,如果有拦截器,就执行拦截方法,如果没有了,就执行请求。- 在4中,
LoadBalancerClient
的execute
方法执行请求,其实现类是RibbonLoadBalancerClient
。execute
先通过serviceId
获取ILoadBalancer
,然后调用ILoadBalancer
的chooseServer
方法,选择一个Server
,并将之转换成RibbonServer
,RibbonServer
是ServiceInstance
的子类,最后调用LoadBalancerRequest
的apply
方法,执行请求,返回响应。