Ribbon 负载均衡
Ribbon是一个内置软件负载平衡器的进程间通信(远程过程调用)库。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,Ribbon是一个客户端负载均衡器,我们可以在配置文件中Load Balancer后面的所有机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器,我们也很容易使用Ribbon实现自定义的负载均衡算法。
Ribbon是一个经过云测试的客户端库。它提供了以下特性
- Load balancing 负载均衡
- Fault tolerance 故障容错
- 异步和响应模型中支持多个协议(HTTP、TCP、UDP)
- 缓存和批处理
Ribbon 核心组件
ServerList:可以响应客户端的特定服务的服务器列表
ServerListFilter:可以动态获得的具有所需特征的候选服务器列表的过滤器
ServerListUpdater:用于执行动态服务器列表更新
IRule:负载均衡策略,用于确定从服务器列表返回哪个服务器
IPing:客户端用于快速检查服务器当时是否处于活动状态(心跳检测)
ILoadBalancer:负载均衡器,负责负载均衡调度的管理
IClientConfig:定义 Ribbon 中管理配置的接口
Ribbon 核心解析
引用依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
注解LoadBalancerClient使用:
@Service
public class SpringDemoRibbonService {
@Autowired
RestTemplate restTemplate;
@Autowired
LoadBalancerClient loadBalancerClient;
@HystrixCommand(fallbackMethod = "portFallback")
public String port() {
this.loadBalancerClient.choose("spring-demo-service"); //随机访问策略
return restTemplate.getForObject("http://SPRING-DEMO-SERVICE/port", String.class);
}
public String portFallback() {
return "sorry ribbon, it's error!";
}
}
1、源码解析:
ServiceInstanceChooser 为指定服务选择一个ServiceInstance
public interface ServiceInstanceChooser {
/**从LoadBalancer中为指定服务选择一个ServiceInstance
* Choose a ServiceInstance from the LoadBalancer for the specified service
*/
ServiceInstance choose(String serviceId);
}
LoadBalancerClient通过继承 ServiceInstanceChooser 实现负载请求
public interface LoadBalancerClient extends ServiceInstanceChooser {
// 使用来自LoadBalancer的ServiceInstance对指定对象执行请求
<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;
<T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;
// 使用真实的主机和端口创建适当的URI
URI reconstructURI(ServiceInstance instance, URI original);
}
LoadBalancerAutoConfiguration-负载均衡配置的自动装配
@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {
/***
* 从类的注解就可以看出,它配置加载的时机
* 一是当前工程环境必须有 RestTemplate 的实 例,
* 二是在工程环境中必须初始化了 LoadBalancerClient 的实现类
**/
@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = 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);
}
}
});
}
@Autowired(required = false)
private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();
/**
* LoadBalancerRequestFactory 用于创建 LoadBalancerRequest和LoadBalancerlnterceptor 使用,它在低版本中是没有的.LoadBalancerInterceptorConfig 中 则 维 护 了 Load- Balancerinterceptor 与RestTemplateCustomizer 的实例 , 它们的作用如下:
LoadBalancerlnterceptor : 拦截每一次 HTTP 请求,将请求绑定进 Ribbon 负载均衡的 生命周期。
RestTemplateCustomizer: 为每个 RestTemplate 绑定 LoadBalancerlnterceptor 拦截器。
**/
@Bean
@ConditionalOnMissingBean
public LoadBalancerRequestFactory loadBalancerRequestFactory(
LoadBalancerClient loadBalancerClient) {
return new LoadBalancerRequestFactory(loadBalancerClient, 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);
};
}
}
}
LoadBalancerInterceptor-负载均衡拦截器
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
private LoadBalancerClient loadBalancer;
private LoadBalancerRequestFactory requestFactory;
/**
* 利用 ClientHttpRequestlnterceptor 来对每次 HTTP 请求进行拦截的,此类是 Spring 中维护的请求拦截器,
* 实现它的 intercept 方法就可以使请求进入方法体,从而做一些处理。
* 可以看出这里把请求拦截下来之后使用了 LoadBalancerClient 的 execute 方法来处理请求
**/
@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, requestFactory.createRequest(request, body, execution));
}
}
LoadBalancerClient实现类RibbonLoadBalancerClient负载均衡
public class RibbonLoadBalancerClient implements LoadBalancerClient {
@Override
public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
// 获取具体的服务实例 就是根基服务的实例名字获取服务的相关元数据信息
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
// Server 就是具体服务实例的封装,就是发生负载均衡过程的地方
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"); // default 就是默认的负载均衡算法的key
}
public Server chooseServer(Object key) {
if (counter == null) {
counter = createCounter();
}
counter.increment();
if (rule == null) {
return null;
} else {
try {
// rule.choose(key) 中的 rule 其实就是 IRule, 至此,拦截的HTTP 请求与负载均衡策略得以关联起来
return rule.choose(key);
} catch (Exception e) {
logger.warn("LoadBalancer [{}]: Error choosing server for key {}", name, key, e);
return null;
}
}
}
}
引用:https://github.com/Netflix/ribbon