一文搞懂流程和原理——Ribbon负载均衡客户端

Ribbon是什么?

一个用于客户端的负载均衡工具

Ribbon怎么用?

@Configuration
public class RibbonConfig{
	@LoadBalanced
	public RestTemplate restTemplate(){
		// ...
	} 
}

只需要在配置类中对RestTemplate的Bean加上@LoadBalanced注解即可。之后在使用RestTemplate进行请求调用时,会自动实现负载均衡

Ribbon如何实现?

RestTemplate的请求拦截

public class RestTemplate extends InterceptingHttpAccessor implements RestOperations

RestTemplate继承了InterceptingHttpAccessor类,其内部持有一系列请求拦截器List,这些拦截器可以对ClientHttpRequestClientHttpResponse进行拦截修改。

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的负载均衡拦截器

Ribbon正是利用了这个拦截器的扩展,为RestTemplate添加了一个拦截器——负载均衡客户端,在创建请求时,会通过负载均衡拿到合适的服务实例地址,再交给RestTemplate请求

LoadBalancerAutoConfiguration中,Ribbon创建了一个负载均衡拦截器LoadBalancerInterceptor

public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor

并创建工具类restTemplateCustomizerLoadBalancerInterceptor添加到了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来执行,即RibbonLoadBalancerClientexecute()方法

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时的默认策略

IRule

ILoadBalancer使用策略接口IRule来决定负载均衡选择的具体规则。

  • RandomRule:随机策略
  • RoundRobinRule:线性遍历策略
  • RetryRule:重试策略
  • WeightedResponseTimeRule:响应时间权重策略
  • ClientConfigEnabledRoundRobinRule:用于继承,当扩展策略不可用时,使用线性遍历策略
  • AvailabilityFilteringRule:对当前服务先过滤的策略
  • ZoneAvoidanceRule:区域感知策略,与Eureka结合的默认策略

IPing

用于监测服务是否可用,在与注册中心结合时会使用NoOpPing策略,取消监测,交由注册中心来实现。

你可能感兴趣的:(SpringCloud)