<p>Ribbon是Netflix公司开源的一个负载均衡的项目,它属于上述的第二种,是一个客户端负载均衡器,运行在客户端上。</p>
<p>在之前介绍使用Ribbon进行服务消费的时候,我们用到了<code>RestTemplate</code>,但是熟悉Spring的同学们是否产生过这样的疑问:<code>RestTemplate</code>不是Spring自己就有的吗?跟Ribbon的客户端负载均衡又有什么关系呢?下面在本文,我们来看<code>RestTemplate</code>和<code>Ribbon</code>是如何联系起来并实现客户端负载均衡的。</p>
<p>首先,回顾一下之前的消费者示例:我们是如何实现客户端负载均衡的?仔细观察一下代码之前的代码,我们可以发现在消费者的例子中,可能就是这个注解<code>@LoadBalanced</code>是之前没有接触过的,并且从命名上来看也与负载均衡相关。我们不妨以此为线索来看看源码实现的机制。</p>
<p>从<code>@LoadBalanced</code>注解源码的注释中,我们可以知道该注解用来给<code>RestTemplate</code>标记,以使用负载均衡的客户端(<code>LoadBalancerClient</code>)来配置它。</p>
<p>通过搜索<code>LoadBalancerClient</code>,我们可以发现这是Spring Cloud中定义的一个接口:</p>
<pre class="blockcode"><code>public interface LoadBalancerClient {
ServiceInstance choose(String serviceId);
<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;
URI reconstructURI(ServiceInstance instance, URI original);
}</code></pre>
<p>从该接口中,我们可以通过定义的抽象方法来了解到客户端负载均衡器中应具备的几种能力:</p>
<ul><li><code>ServiceInstance choose(String serviceId)</code>:根据传入的服务名<code>serviceId</code>,从负载均衡器中挑选一个对应服务的实例。</li><li><code>T execute(String serviceId, LoadBalancerRequest request) throws IOException</code>:使用从负载均衡器中挑选出的服务实例来执行请求内容。</li><li><code>URI reconstructURI(ServiceInstance instance, URI original)</code>:为系统构建一个合适的“host:port”形式的URI。在分布式系统中,我们使用逻辑上的服务名称作为host来构建URI(替代服务实例的“host:port”形式)进行请求,比如:<code>http://myservice/path/to/service</code>。在该操作的定义中,前者<code>ServiceInstance</code>对象是带有host和port的具体服务实例,而后者URI对象则是使用逻辑服务名定义为host的URI,而返回的URI内容则是通过<code>ServiceInstance</code>的服务实例详情拼接出的具体“host:post”形式的请求地址。</li></ul>
<p>顺着<code>LoadBalancerClient</code>接口的所属包<code>org.springframework.cloud.client.loadbalancer</code>,我们对其内容进行整理,可以得出如下图的关系:</p>
<p><img alt="" class="blockcode" src="https://beijingoptbbs.oss-cn-beijing.aliyuncs.com/cs/5606289-f5b37e550a0a2e4b887ea5658e21b351.png"></p>
<p>从类的命名上我们初步判断<code>LoadBalancerAutoConfiguration</code>为实现客户端负载均衡器的自动化配置类。通过查看源码,我们可以验证这一点假设:</p>
<pre class="blockcode"><code>@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
public class LoadBalancerAutoConfiguration {
@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();
@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializer(
final List<RestTemplateCustomizer> customizers) {
return new SmartInitializingSingleton() {
@Override
public void afterSingletonsInstantiated() {
for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
for (RestTemplateCustomizer customizer : customizers) {
customizer.customize(restTemplate);
}
}
}
};
}
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor) {
return new RestTemplateCustomizer() {
@Override
public void customize(RestTemplate restTemplate) {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
}
};
}
@Bean
public LoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient) {
return new LoadBalancerInterceptor(loadBalancerClient);
}
}</code></pre>
<p>从<code>LoadBalancerAutoConfiguration</code>类头上的注解可以知道Ribbon实现的负载均衡自动化配置需要满足下面两个条件:</p>
<ul><li><code>@ConditionalOnClass(RestTemplate.cl |
|