Ribbon源码分析

29 篇文章 21 订阅
16 篇文章 8 订阅

Ribbon是Netflix公司开源的一个客户端负载均衡的项目,一般配合Eureka使用。不过为了降低其他干扰因素,专注于Ribbon,这一次我们脱离Eureka讲Ribbon。

上一篇我们讲了RestTemplate源码分析,今天这一篇打算使用@LoadBalanced注解使得RestTemplate具有负载均衡的能力。

一、简单的例子

首先引入ribbon的依赖

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
            <version>2.1.4.RELEASE</version>
        </dependency>

接着直接为RestTemplate的Bean打上@LoadBalanced的注解

@Configuration
public class RestTemplateConfig {

    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }

}

项目配置,demo是负载均衡器访问的服务名称

我们接下来会在8081与8082端口启动两个服务demo1与demo2,不过他们的application.name都为demo

server.port=8080
demo.ribbon.listOfServers=http://localhost:8081,http://localhost:8082

在Controller中直接访问demo服务

@RestController
public class Controller {

    @Autowired
    RestTemplate restTemplate;

    @RequestMapping("/test")
    public String test() {
        String result = restTemplate.getForObject("http://demo/test", String.class);
        System.out.println(result);
        return result;
    }
}

这个是demo1项目的controller,demo2返回8082

@RestController
public class Controller {

    @RequestMapping("/test")
    public String test() {
        return "8081";
    }
}

demo1项目的配置,demo2的端口为8082,name依然为demo

spring.application.name=demo
server.port=8081

这个时候我们访问localhost:8080/test

依次返回8081与8082,证明RestTemplate具有了负载均衡的能力。

当我们关闭demo2,再进行多次调用时,发现会依次返回8081与500错误。

ps:请记住这里的现象,之后会用源码进行解释为什么会出现这样的现象。


二、@LoadBalanced注解内幕

LoadBalanced是个组合注解,点进去发现

/**
 * Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient.
 * @author Spencer Gibb
 */
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {

}

通过最上面的注释,我们可以了解到:

LoadBalance标记的RestTemplate,这个RestTemplate之后将会使用LoadBalancerClient来配置自己。

在idea中双击Shift打开全局搜索,可以找到该接口。

public interface LoadBalancerClient extends ServiceInstanceChooser

其中ServiceInstanceChooser接口中只有一个抽象方法:

public interface ServiceInstanceChooser {
	ServiceInstance choose(String serviceId);
}

该方法由传入的服务id,会从负载均衡器中挑选出来一个服务实例,服务实例使用ServiceInstance封装。

ServiceInstance的方法如下:

 LoadBalancerClient有以下的抽象方法:

public interface LoadBalancerClient extends ServiceInstanceChooser {

    //从负载均衡器中挑选出来一个服务实例,然后请求该实例
	<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;
	<T> T execute(String serviceId, ServiceInstance serviceInstance,
			LoadBalancerRequest<T> request) throws IOException;

    //重构url,即将服务名称转化为ip:port的形式,instance参数决定使用哪个ip端口
	URI reconstructURI(ServiceInstance instance, URI original);

}

在@LoadBalanced注解、LoadBalancerClient与ServiceInstanceChooser旁边,我们还发现了LoadBalancerAutoConfiguration,他是一个负载均衡器的自动配置类。


三、LoadBalancerAutoConfiguration自动配置类分析

LoadBalancerAutoConfiguration源码如下:

@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {

	@LoadBalanced
	@Autowired(required = false)
	private List<RestTemplate> restTemplates = Collections.emptyList();

	@Autowired(required = false)
	private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();

	@Bean
	public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
			final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
		return () -> restTemplateCustomizers.ifAvailable(customizers -> {
			for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
				for (RestTemplateCustomizer customizer : customizers) {
					customizer.customize(restTemplate);
				}
			}
		});
	}

	@Bean
	@ConditionalOnMissingBean
	public LoadBalancerRequestFactory loadBalancerRequestFactory(
			LoadBalancerClient loadBalancerClient) {
		return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);
	}

	@Configuration
	@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
	static class LoadBalancerInterceptorConfig {

		@Bean
		public LoadBalancerInterceptor ribbonInterceptor(
				LoadBalancerClient loadBalancerClient,
				LoadBalancerRequestFactory requestFactory) {
			return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
		}

		@Bean
		@ConditionalOnMissingBean
		public RestTemplateCustomizer restTemplateCustomizer(
				final LoadBalancerInterceptor loadBalancerInterceptor) {
			return restTemplate -> {
				List<ClientHttpRequestInterceptor> list = new ArrayList<>(
						restTemplate.getInterceptors());
				list.add(loadBalancerInterceptor);
				restTemplate.setInterceptors(list);
			};
		}

	}


	@Configuration
	@ConditionalOnClass(RetryTemplate.class)
	public static class RetryAutoConfiguration {
      ....
	}

	@Configuration
	@ConditionalOnClass(RetryTemplate.class)
	public static class RetryInterceptorAutoConfiguration {
      ...
	}

}

@Configuration代表该类是一个配置类

@ConditionalOnClass(RestTemplate.class)

@ConditionalOnBean(LoadBalancerClient.class)

这两个注解则说明如果要实现Ribbon的自动配置,则需要能加载到RestTemplate类,以及存在LoadBalancerClient的接口实现类。

RestTemplate我们在一开始就注入到容器当中了,而我们引入的netflix.ribbon依赖包中有LoadBalancerClient的接口实现类,即RibbonLoadBalancerClient,这个类我们后面再讲。

	@LoadBalanced
	@Autowired(required = false)
	private List<RestTemplate> restTemplates = Collections.emptyList();

使用@Autowired加@LoadBalanced注解,将会使得容器向restTemplates 集合中注入被@LoadBalanced注解修饰的RestTemplate。

我们把RetryAutoConfiguration和RetryInterceptorAutoConfiguration自动配置代码省略掉了,因为@ConditionalOnClass(RetryTemplate.class)需要当前项目存在RetryTemplate类,但是我们并没有引入。

在静态类LoadBalancerInterceptorConfig 中,就做了两件事:

(1)向容器中注入LoadBalancerInterceptor拦截器

		@Bean
		public LoadBalancerInterceptor ribbonInterceptor(
				LoadBalancerClient loadBalancerClient,
				LoadBalancerRequestFactory requestFactory) {
			return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
		}

(2)返回一个RestTemplateCustomizer的实现类,只不过使用lambda简化了代码。之后获取入参restTemplate的所有拦截器集合,并将当前loadBalancerInterceptor拦截器加入到集合中,最后使用setInterceptors(list)保存当前拦截器集合

		@Bean
		@ConditionalOnMissingBean
		public RestTemplateCustomizer restTemplateCustomizer(
				final LoadBalancerInterceptor loadBalancerInterceptor) {
			return restTemplate -> {
				List<ClientHttpRequestInterceptor> list = new ArrayList<>(
						restTemplate.getInterceptors());
				list.add(loadBalancerInterceptor);
				restTemplate.setInterceptors(list);
			};
		}

loadBalancedRestTemplateInitializerDeprecated方法,入参是所有RestTemplateCustomizer的实现类(上文刚使用@Bean将返回的RestTemplateCustomizer的实现类注入到了容器中)。

遍历所有被@LoadBalanced修饰的RestTemplate,依次调用customize方法,即为RestTemplate添加LoadBalancerInterceptor拦截器。

	public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
			final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
		return () -> restTemplateCustomizers.ifAvailable(customizers -> {
			for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
				for (RestTemplateCustomizer customizer : customizers) {
					customizer.customize(restTemplate);
				}
			}
		});
	}

LoadBalancerInterceptor拦截器内部到底做了什么呢?


四、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 {
        //获取要访问的地址,即例子中的http://demo/test
		final URI originalUri = request.getURI();
        //获取服务名称,即demo
		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));
	}

}

跟进到loadBalancer.execute方法中,loadBalancer即LoadBalancerClient的唯一实现类RibbonLoadBalancerClient

	public <T> T execute(String serviceId, LoadBalancerRequest<T> request)
			throws IOException {
		return execute(serviceId, request, null);
	}

继续进入RibbonLoadBalancerClient的execute方法

	public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
			throws IOException {
		ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
		Server server = getServer(loadBalancer, hint);
		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);
	}

ILoadBalancer loadBalancer = getLoadBalancer(serviceId);

从字面意思来看,应该是获取负载均衡器...(不会有人看不出来吧),也就是接口ILoadBalancer的实现类。

深入getLoadBalancer内部,其实是从容器中寻找ILoadBalancer 的实现类。

以下是ILoadBalancer及依赖类的类图:

 

 那到底是哪个实现类呢?

其实ILoadBalancer 的实现类在自动配置类RibbonClientConfiguration中完成了注入:

	@Bean
	@ConditionalOnMissingBean
	public ILoadBalancer ribbonLoadBalancer(IClientConfig config,
			ServerList<Server> serverList, ServerListFilter<Server> serverListFilter,
			IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
		if (this.propertiesFactory.isSet(ILoadBalancer.class, name)) {
			return this.propertiesFactory.get(ILoadBalancer.class, config, name);
		}
		return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList,
				serverListFilter, serverListUpdater);
	}

是ZoneAwareLoadBalancer类!


五、ZoneAwareLoadBalancer是怎么构造的

构造该类需要以下参数(这些参数在RibbonClientConfiguration中全部提前注入到容器中了)

(1)IClientConfig config,是对客户端负载均衡器的配置,包括默认的读取超时时间(5s)、连接超时(2s)与最大连接数(200)等等,默认的实现类是DefaultClientConfigImpl。

(2)ServerList<Server> serverList,获取服务列表。目前我们是在yaml中写死了服务地址集合,因此serverList的类型实际上是ConfigurationBasedServerList,即从配置文件中获取服务地址的集合。

(3)ServerListFilter<Server> serverListFilter,服务列表过滤器,默认类型是ZonePreferenceServerListFilter,将不和客户端在同一个zone的服务给过滤掉。我们也没配置过什么zone,因此该过滤器在这次的例子中,其实是没什么作用的。

(4)IRule rule,是负载均衡策略接口,常见的策略有RoundRobinRule(轮询),RandomRule(随机),默认采用ZoneAvoidanceRule,即按照zone筛选,再进行轮询。(本例中,表现出来的效果就是一直轮询,所以接口依次返回8081、8082)

IRule及其实现类的类图关系如下:

 (5)IPing ping,判断服务实例是否存活的接口,常见的实现类有DummyPing(直接返回true,永远认为服务正常)、PingUrl(真实去ping某个url,得到存活与否)等等。默认探活策略是DummyPing,所以就造成了我们即使关闭demo2服务,ribbon也依然会选择到demo2服务。

IPing及其实现类的类图如下:

 (6)ServerListUpdater serverListUpdater,用于执行对服务列表的更新操作,默认的实现是PollingServerListUpdater,会启动一个ScheduledThreadPoolExecutor,周期性的执行IPing策略。(对线程池不熟悉的同学,可以参考我的这篇文章说说线程池


六、execute方法

我把RibbonLoadBalancerClient的execute方法再贴一遍

	public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
			throws IOException {
		ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
		Server server = getServer(loadBalancer, hint);
		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);
	}

从上文,可以了解到:

(1)getLoadBalancer(serviceId):可以理解为,再第一次请求到来时,创建好IClientConfig(客户端配置)、ServerList<Server>(从配置文件中加载的服务列表)、IRule(负载均衡策略)与IPing (探活策略)等Bean,是一种懒加载的模式。

(2)getServer(loadBalancer, hint):则是通过以上的负载均衡策略与探活策略,从服务列表中选择合适的服务实例(详细代码在ZoneAwareLoadBalancer的chooseServer方法中)。Server对象包含ip、端口与协议等信息。

进入到execute(serviceId, ribbonServer, request)方法中:

其核心代码是apply方法:

T returnVal = request.apply(serviceInstance)

其实接下来的代码,已经和Ribbon没有太大的关系了。

LoadBalancerInterceptor的intercept方法已经全部走完了,接下会在InterceptingClientHttpRequest中的execute方法内遍历其他拦截器,走下一个拦截器的intercept方法。

如果此时没有其他拦截器,最终会走RestTemplate的执行流程。此时RestTemplate已经拿到了负载均衡后的地址,利用封装的HttpURLConnection直接进行请求。


七、RestTemplate是怎么利用到该拦截器的?

上一篇我们讲了RestTemplate源码分析,举的例子是没有用到任何拦截器的。

那么用到拦截器呢,具体代码我们从RestTemplate的主流程方法doExecute说起

	protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
			@Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
 
		Assert.notNull(url, "URI is required");
		Assert.notNull(method, "HttpMethod is required");
		ClientHttpResponse response = null;
		try {
            //创建文章开头所说的ClientHttpRequest 
			ClientHttpRequest request = createRequest(url, method);
			if (requestCallback != null) {
                //执行请求回调
				requestCallback.doWithRequest(request);
			}
            //执行请求,U获取响应结果
			response = request.execute();
            //处理响应结果
			handleResponse(url, method, response);
            //利用响应抽取器抽取data返回预先定义的java对象,例如例子中的String
			return (responseExtractor != null ? responseExtractor.extractData(response) : null);
		}
		catch (IOException ex) {
			String resource = url.toString();
			String query = url.getRawQuery();
			resource = (query != null ? resource.substring(0, resource.indexOf('?')) : resource);
			throw new ResourceAccessException("I/O error on " + method.name() +
					" request for \"" + resource + "\": " + ex.getMessage(), ex);
		}
		finally {
			if (response != null) {
				response.close();
			}
		}
	}

进入到ClientHttpRequest request = createRequest(url, method)方法中

此处调用的是HttpAccessor中的createRequest方法

    protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
        ClientHttpRequest request = this.getRequestFactory().createRequest(url, method);
        this.initialize(request);
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("HTTP " + method.name() + " " + url);
        }
 
        return request;
    }

其中getRequestFactory方法被InterceptingHttpAccessor重写了

	public ClientHttpRequestFactory getRequestFactory() {
		List<ClientHttpRequestInterceptor> interceptors = getInterceptors();
		if (!CollectionUtils.isEmpty(interceptors)) {
			ClientHttpRequestFactory factory = this.interceptingRequestFactory;
			if (factory == null) {
				factory = new InterceptingClientHttpRequestFactory(super.getRequestFactory(), interceptors);
				this.interceptingRequestFactory = factory;
			}
			return factory;
		}
		else {
			return super.getRequestFactory();
		}
	}

因为此时的getInterceptors返回的拦截器不为空,则此时获取到请求工厂为InterceptingClientHttpRequestFactory,而不是无拦截器时的SimpleClientHttpRequestFactory。

InterceptingClientHttpRequestFactory的createRequest方法会创建出InterceptingClientHttpRequest,而不是默认的SimpleBufferingClientHttpRequest

两者的类图关系如下:

 接下来走RestTemplate主流程中的response = request.execute()方法

这一块和SimpleBufferingClientHttpRequest一样,进入到父类AbstractClientHttpRequest中

	public final ClientHttpResponse execute() throws IOException {
		assertNotExecuted();
		ClientHttpResponse result = executeInternal(this.headers);
		this.executed = true;
		return result;
	}

还是和SimpleBufferingClientHttpRequest一样,进入executeInternal方法中,位于AbstractBufferingClientHttpRequest中

	protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException {
		byte[] bytes = this.bufferedOutput.toByteArray();
		if (headers.getContentLength() < 0) {
			headers.setContentLength(bytes.length);
		}
		ClientHttpResponse result = executeInternal(headers, bytes);
		this.bufferedOutput = new ByteArrayOutputStream(0);
		return result;
	}

其中核心的是executeInternal(headers, bytes)方法,位于InterceptingClientHttpRequest中

	protected final ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
		InterceptingRequestExecution requestExecution = new InterceptingRequestExecution();
		return requestExecution.execute(this, bufferedOutput);
	}

进入InterceptingRequestExecution(其实就是InterceptingClientHttpRequest的私有内部类)的execute方法中

		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 {
				HttpMethod method = request.getMethod();
				Assert.state(method != null, "No standard HTTP method");
				ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), method);
				request.getHeaders().forEach((key, value) -> delegate.getHeaders().addAll(key, value));
				if (body.length > 0) {
					if (delegate instanceof StreamingHttpOutputMessage) {
						StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) delegate;
						streamingOutputMessage.setBody(outputStream -> StreamUtils.copy(body, outputStream));
					}
					else {
						StreamUtils.copy(body, delegate.getBody());
					}
				}
				return delegate.execute();
			}
		}

this.iterator是当前拦截器的迭代器,如果当前有拦截器的话,直接先执行拦截器intercept方法,而此时的拦截器类型就是LoadBalancerInterceptor,这样的话,接下来的内容又回到了第四章节中。

LoadBalancerInterceptor拦截器执行完之后,又会回到该execute方法中。接下来走else逻辑,此时delegate类型是SimpleBufferingClientHttpRequest,真是熟悉的类啊,此时场景就变成了没有拦截器的场景了。

如果要接着跟进的话,可以从RestTemplate源码分析这个环节开始:

到这里,RestTemplate如何利用到Ribbon提供的负载均衡能力的过程已经结束了。


八、Ribbon的大致流程总结

(1)Ribbon的自动配置类拿到所有被@LoadBalanced注解修饰的RestTemplate实例

(2)将LoadBalancerInterceptor拦截器添加到每一个RestTemplate的拦截器列表中

(3)RestTemplate在执行请求前,会先执行每一个拦截器的intercept方法

(4)LoadBalancerInterceptor的intercept方法中,首先会从配置文件中读取服务实例集合,接着创建负载均衡策略、探活策略与服务列表更新策略等

(5)接着intercept方法会根据以上的策略选取一个服务实例

(6)RestTemplate拿到该服务实例后,内部利用封装的HttpURLConnection进行请求

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SunAlwaysOnline

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值