Netflix Ribbon - 负载均衡器拦截器

103 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。


拦截器机制改变RestTemplate的行为

LoadBalancerAutoConfiguration通过RestTemplateCustomizer给RestTemplate设置一个拦截器(LoadBalancerInterceptor)。

如果对RestTemplate执行某个操作,相当于发起一个http请求,此时不会由RestTemplate自己原生的功能来实现,而是会由拦截器来实现这个请求http服务的一个功能。

LoadBalancerInterceptor拦截器

public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {

	private LoadBalancerClient loadBalancer;

	private LoadBalancerRequestFactory requestFactory;

	public LoadBalancerInterceptor(LoadBalancerClient loadBalancer,
			LoadBalancerRequestFactory requestFactory) {
		this.loadBalancer = loadBalancer;
		this.requestFactory = requestFactory;
	}

	public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
		// for backwards compatibility
		this(loadBalancer, new LoadBalancerRequestFactory(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();
		Assert.state(serviceName != null,
				"Request URI does not contain a valid hostname: " + originalUri);
		return this.loadBalancer.execute(serviceName,
				this.requestFactory.createRequest(request, body, execution));
	}

}

在执行下面那行代码的时候,其实是将这个请求给封装了一下,将 http://ServiceA/product/ 封装到了HttpRequest(HTTP请求报文)里面去,然后将HttpRequest加上请求体的字节数组,ClientHttpRequestExecution(HTTP请求报文客户端执行,包含一个拦截器集合的迭代器,用来迭代执行拦截器)

restTemplate.getForObject("http://ServiceA/product/", String.class);

restTemplate 是实现了 InterceptingHttpAccessor 接口的,具备支持拦截器的能力。

相当于是底层调用了拦截器里的intercept()方法,实际的这个请求的逻辑,不再由RestTemplate原来原生的默认的逻辑来实现,而是由intercept()拦截方法来实现了。

final URI originalUri = request.getURI();

String serviceName = originalUri.getHost();

request.getURI(),获取到的就是:http://ServiceA/product/

originalUri.getHost(),获取到的就是:ServiceA

serviceName,服务名称

Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);

如果你的获取到的服务名称是null,那么就打印异常日志,告诉你,你的请求的url地址里面没有包含合格的hostname主机名

在构造LoadBalancerInterceptor的时候,其实传进来了一个LoadBalancerClient。

LoadBalancerInterceptor,拦截掉RestTemplate所有的执行的请求,然后从url地址里获取hostname作为服务名称,然后找LoadBalancerClient去执行对应的负载均衡的请求

LoadBalancerClient 实现 ServiceInstanceChooser接口

1. ServiceInstance choose(String serviceId);从LoadBalancer中更具ServiceId获取一个具体的服务实例
2. <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;根据服务实例,LoadBalancerRequest(负载均衡器请求报文)执行请求
3. URI reconstructURI(ServiceInstance instance, URI original);根据服务实例和原来的URI重新构造一个URI。