SpringCloud客户端负载均衡——Ribbon

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服务中,通过调用RestTemplategetForEntity方法,GET调用hello-service/hello接口。

RestTemplate

GET

RestTemplate有两类GET实现:getForEntitygetForObject

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实现:postForEntitypostForObjectpostForLocation

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

RestTemplatedoExecute()方法中,调用request.execute()之前,会被LoadBalancerInterceptor拦截。该拦截器中有一个LoadBalancerClient实例,此外该拦截器在LoadBalancerAutoConfiguration中被创建。而LoadBalancerAutoConfiguration有两个特殊的注解@ConditionalOnClass(RestTemplate.class)@ConditionalOnBean(LoadBalancerClient.class),且注释明确说明LoadBalancerAutoConfigurationRibbon的自动化配置类。

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,创建ServerIntrospector
  • RibbonAutoConfiguration:创建HasFeatures,创建SpringClientFactory,创建LoadBalancerClient(使用RibbonLoadBalancerClient实现),创建LoadBalancedRetryFactory(使用RibbonLoadBalancedRetryFactory实现),创建PropertiesFactory,创建RibbonApplicationContextInitializer,创建RestTemplateCustomizer(使用匿名内部类),创建RibbonClientHttpRequestFactory
  • RestCilentRibbonConfiguration:创建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一起作为LoadBalancerClientexecute()方法的入参。

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继承自HttpRequestWrapperHttpRequestWrapper对外提供了获取一个request的methodURIheadersmethodValue等信息的方法。
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实现了LoadBalancerClientServiceInstanceChooser中的executereconstructURIchoose方法,完成了请求执行、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

RibbonLoadBalancerClientchoose()execute()方法中,都是通过调用ILoadBalancerchooseServer()方法,来选择一个服务实例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、元数据等等。
RibbonLoadBalancerClientchoose()execute()方法中,通过getLoadBalancer()方法,来根据serviceId获取ILoadBalancer的实例,然后将其包装成RibbonServer
配置类RibbonClientConfiguration创建ILoadBalancer时如果配置文件里有配置,则使用配置的实现,否则默认使用ZoneAwareLoadBalancer实现。

ClientHttpRequestExecution

RibbonLoadBalancerClientexecute()方法中,调用了入参LoadBalancerRequest的apply方法,execute()方法在LoadBlancerInterceptorintercept方法中调用,并传入LoadBalancerRequestFactory.createRequest创建的LoadBalancerRequest实现,其实现中最终使用ClientHttpRequestExecutionexecute方法执行请求。

总结

  1. 使用时,注册一个使用@LoadBalanced注解修饰的RestTemplate,在需要发起请求的地方调用RestTemplate的相应的请求方法,最终调用到其doExecute方法。
  2. @LoadBalanced注解关联了LoadBalancerClient
  3. 配置类LoadBalancerAutoConfiguration注册了如下Bean:
    • SmartInitializingSingleton:遍历restTemplates、遍历customizers,并customizer.customize(restTemplate)
    • LoadBalancerRequestFactory:使用LoadBalancerClient构造
    • LoadBalancerInterceptor:使用LoadBalancerClientLoadBalancerRequestFactory创建
    • RestTemplateCustomizer:使用LoadBalancerInterceptor构造一个匿名类,将注册的LoadBalancerInterceptor添加进restTemplateinterceptors列表中
  4. 拦截器LoadBalancerInterceptorintercept方法从原始请求中获取URI,然后使用LoadBalancerClientexecute方法执行请求,接收两个参数:serviceName即host和请求工厂创建的request
  5. 请求工厂LoadBalancerRequestFactorycreateRequest方法,由原始请求创建一个LoadBalancerRequest的匿名实现
  6. 负载均衡请求LoadBalancerRequest接口只有apply方法,其匿名实现创建HttpRequest的实现类ServcieRequestWrapper的实例,然后由ClientHttpRequestExecutionexecute方法执行请求,返回响应ClientHttpResponse
  7. ServiceRequestWrapper重写了父类HttpRequestWrappergetURI方法,返回LoadBalancerClientreconstructURI方法重构的URI
  8. ClientHttpRequestExecution的实现类是InterceptingClientHttpRequest的内部类IntercpetingRequestExecution,其execute方法遍历interceptors,如果有拦截器,就执行拦截方法,如果没有了,就执行请求。
  9. 在4中,LoadBalancerClientexecute方法执行请求,其实现类是RibbonLoadBalancerClientexecute 先通过serviceId获取ILoadBalancer,然后调用ILoadBalancerchooseServer方法,选择一个Server,并将之转换成RibbonServerRibbonServerServiceInstance的子类,最后调用LoadBalancerRequestapply方法,执行请求,返回响应。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值