微服务架构之均衡组件 Ribbon解析:和RestTemplate一起使用
Ribbon
可以和RestTemplate
一起使用,也可以集成到Feign
中使用。下面的文章将会分别讲述Ribbon
在这两种使用方法下的示例。
和RestTemplate一起使用
Ribbon
和RestTemplate
之间存在一定的关系,而使二者产生联系的就是@LoadBalanced
注解。@LoadBalanced
是一个标记注解,可以表明被修饰的RestTemplate
应该使用RibbonLoadBalancerClient
来发送HTTP请求。RibbonLoadBalancerClient
是Ribbon Client实例,用来进行HTTP请求的发送和响应的接受,后续的文章将会对它进行讲解。
/**
* 使用这个注解标注RestTemplate bean可以让LoadBalanceClient来配置这个RestTemplate
*/
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}
Ribbon是如何识别被
@LoadBalanced标注的
RestTemplate实例的呢?如何将
Ribbon相关的拦截器添加到
RestTemplate中呢?关键在于
LoadBalancerAutoConfiguration这个配置类中。
LoadBalancerAutoConfiguration类初始化一个匿名的
SmartInitializingSingleton实例在所有的
RestTemplate实例初始化结束之后对该进行配置。根据
Spring框架的原理,
SmartInitializingSingleton的
afterSingletonsInstantiated`会在所有单例类型的Bean实例创建完成之后被调用。
在afterSingletonsInstantiated
中,Ribbon
使用RestTemplateCustomizer
对RestTemplate
实例进行配置,加入自己的拦截器。
@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
//只有当RestTemplate class和LoadBalancerClient的bean都存在时才执行。LoadBalancerClient会在RibbonAutoConfiguration配置类中给出。
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {
@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();
/**
* 创建SmartInitializingSingleton接口的实现类。Spring会在所有
* 单例Bean初始化完成后回调该实现类的afterSingletonsInstantiated()
* 方法。在这个方法中会为所有被@LoadBalanced注解标识的
* RestTemplate添加ribbon的自定义拦截器LoadBalancerInterceptor。
*/
@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);
}
}
}
};
}
}
通过LoadBalancerInterceptorConfig
或者RetryAutoConfiguration
配置类的RestTemplateCustomizer
函数,Ribbon提供了对RestTemplate
进行配置的RestTemplateCustomizer
实例。它会将Ribbon自己的LoadBalancerInterceptor
实例添加到RestTemplate
的HTTP拦截器列表中。
/**
* 创建Ribbon自定义拦截器LoadBalancerInterceptor
* 创建前提是当前classpath下不存在spring-retry。
* 所以LoadBalancerInterceptor是默认的Ribbon拦截
* 请求的拦截器。
*/
@Configuration
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
static class LoadBalancerInterceptorConfig {
@Bean
public LoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRequestFactory requestFactory) {
//提供Ribbon实现的拦截器
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
/**
* 添加拦截器具体方法。首先获取当前拦截器集合(List)
* 然后将loadBalancerInterceptor添加到当前集合中
* 最后将新的集合放回到restTemplate中。
*/
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor) {
return new RestTemplateCustomizer() {
@Override
public void customize(RestTemplate restTemplate) {
//将自己的LoadBalancerInterceptor拦截器添加到RestTemplate实例中
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
}
};
}
}
LoadBalancerInterceptor
实现了ClientHttpRequestInterceptor
接口。ClientHttpRequestInterceptor
接口是客户端HTTP请求拦截器接口,它可以被注册给RestTemplate
实例上,来修改发送的ClientHttpRequest
对象和接受的ClientHttpResponse
对象。
public interface ClientHttpRequestInterceptor {
/**
*实现类可以实现该方法,在该方法内完成拦截请求后的逻辑内容。
*对于Ribbon而言,在该方法内完成了根据具体规则从服务集群中选取一个服务,并向该服务发起请求的操作。
*/
ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException;
}
LoadBalancerInterceptor
会在RestTemplate
发送网络请求时使用LoadBalancerClient
实例来代替RestTemplate
进行实际的网络请求的发送和处理。LoadBalancerRequestFactory
会将HttpRequest
转化为LoadbalancerRequest
。
//LoadBalancerInterceptor是Ribbon对于ClientHttpRequestInterceptor接口的实现类。
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) {
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();
//将请求交给LoadBalancerClient来执行,具体执行过程之后会详解。
return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution));
}
}
如上的代码即可实现 Ribbon 与RestTemplate一起使用。
小结
Ribbon
使用的是客户端负载均衡。客户端负载均衡和服务端负载均衡最大的区别在于服务清单所存储的位置,在客户端负载均衡中,所有的客户端节点都有一份自己要访问的服务端地址列表,这些列表统统都是从Eureka服务注册中心获取的。在Spring Cloud
中我们如果想要使用客户端负载均衡,方法很简单,开启@LoadBalanced
注解即可,这样客户端在发起请求的时候会先自行选择一个服务端,向该服务端发起请求,从而实现负载均衡。
- 点赞
- 收藏
- 关注作者
评论(0)