负载均衡原理,探究@LoadBalanced注解都做了什么(Ribbon)
Posted 牛哄哄的柯南
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了负载均衡原理,探究@LoadBalanced注解都做了什么(Ribbon)相关的知识,希望对你有一定的参考价值。
负载均衡原理,探究@LoadBalanced注解都做了什么
RPC-百度百科
RPC(Remote Procedure Call Protocol)–远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。
RPC采用客户机/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息的到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。
RPC是远程过程调用(Remote Procedure Call)的缩写形式。SAP系统RPC调用的原理其实很简单,有一些类似于三层构架的C/S系统,第三方的客户程序通过接口调用SAP内部的标准或自定义函数,获得函数返回的数据进行处理后显示或打印。
负载均衡原理
定义
负载均衡建立在现有网络结构之上,它提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽、增加吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性。
负载均衡(Load Balance)其意思就是分摊到多个操作单元上进行执行,例如Web服务器、FTP服务器、企业关键应用服务器和其它关键任务服务器等,从而共同完成工作任务。
负载均衡是一个通用的特性,所有的RPC框架都会有这个概念的实现。
平时说负载均衡一般都是指服务端负载均衡,但因为分布式spring cloud分布式框架出现,也出现了客户端负载均衡这一概念
服务端负载均衡
最常见的就是nginx,客户端发送请求,由Nginx服务器接收,根据使用的负载均衡算法,再将请求发送给相应的应用服务器。
由Nginx分配到不同的服务端
客户端负载均衡
客户端的负载均衡是在spring-cloud后出现的,在spring-cloud中有ribbon组件来负责负载均衡。spring的负载均衡需要用到服务注册中心eruka。
服务提供方:将自己注册到服务注册中心eruka
服务消费方:从服务注册中心中获取服务列表,使用服务
客户端的负载均衡流程如下:
服务消费方通过ribbon先从服务注册中心获取服务列表,根据一定的负载均衡算法,分发请求到不同的服务提供方
客户端从服务中心选择一个,去调用
参考自:http://t.csdn.cn/zBP3x
常见的负载均衡算法
当然,我们可以去自定义负载均衡算法。
Ribbon负载均衡组件
Ribbon(spring-cloud-starter-ribbon)
我们都知道使用RestTemplate
时,可以直接使用服务名进行服务调用,只需要在定义RestTemplate
时加上@LoadBalanced
注解就可以了
@LoadBalanced //配置负载均衡实现RestTemplate
@Bean
public RestTemplate getRestTemplate()
return new RestTemplate();
接下来,我们重点分析下@LoadBalanced
这个注解底层都干了什么
@LoadBalanced
在自动配置类里我们可以看到LoadBalancerAutoConfiguration
的存在。
LoadBalancerAutoConfiguration
我们先看下这个类的源码:
@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration
@LoadBalanced
@Autowired(
required = false
)
//这里是从ApplicationContext中获取所有被@LoadBalanced修饰的RestTemplate
private List<RestTemplate> restTemplates = Collections.emptyList();
@Autowired(
required = false
)
private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();
public LoadBalancerAutoConfiguration()
@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers)
//这里的 List<RestTemplateCustomizer> 是 ApplicationContext 存在的 RestTemplateCustomizer集合
return () ->
restTemplateCustomizers.ifAvailable((customizers) ->
Iterator var2 = this.restTemplates.iterator();
while(var2.hasNext())
RestTemplate restTemplate = (RestTemplate)var2.next();
Iterator var4 = customizers.iterator();
while(var4.hasNext())
RestTemplateCustomizer customizer = (RestTemplateCustomizer)var4.next();
//遍历restTemplates集合,使用RestTemplateCustomizer给每个restTemplate定制一下
customizer.customize(restTemplate);
);
;
...
接着看这个RestTemplateCustomizer定制过程做了啥
RestTemplateCustomizer
RestTemplateCustomizer 是 LoadBalancerAutoConfiguration 的内部类
@Configuration
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
static class LoadBalancerInterceptorConfig
LoadBalancerInterceptorConfig()
//定义LoadBalancerInterceptor Bean, 这个拦截器继承自 ClientHttpRequestInterceptor ,
//可以被添加到RestTemplate的拦截器列表中
//public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor
@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());
//在RestTemplate的拦截器列表中加上LoadBalancerInterceptor拦截器
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
;
这里就是 @LoadBalanced 直接修饰的 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)
this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException
// 服务名使用URI中的host信息
URI originalUri = request.getURI();
String serviceName = originalUri.getHost();
Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
//使用LoadBalancerClient 客户端负载均衡器做真正的服务调用
return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
LoadBalancerClient
LoadBalancerClient (客户端负载均衡器)会根据负载均衡请求和服务名做真正的负载均衡。
public interface LoadBalancerClient extends ServiceInstanceChooser
//serviceId就是服务名,request就是请求
<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;
// serviceInstance 服务实例
<T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;
//带有服务名的老的URI
URI reconstructURI(ServiceInstance instance, URI original);
public interface LoadBalancerRequest<T>
T apply(ServiceInstance instance) throws Exception;
public class LoadBalancerRequestFactory
private LoadBalancerClient loadBalancer;
private List<LoadBalancerRequestTransformer> transformers;
public LoadBalancerRequestFactory(LoadBalancerClient loadBalancer, List<LoadBalancerRequestTransformer> transformers)
this.loadBalancer = loadBalancer;
this.transformers = transformers;
public LoadBalancerRequestFactory(LoadBalancerClient loadBalancer)
this.loadBalancer = loadBalancer;
public LoadBalancerRequest<ClientHttpResponse> createRequest(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution)
return (instance) ->
HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance, this.loadBalancer);
LoadBalancerRequestTransformer transformer;
if (this.transformers != null)
for(Iterator var6 = this.transformers.iterator(); var6.hasNext(); serviceRequest = transformer.transformRequest((HttpRequest)serviceRequest, instance))
transformer = (LoadBalancerRequestTransformer)var6.next();
return execution.execute((HttpRequest)serviceRequest, body);
;
RibbonLoadBalancerClient
Ribbon 中 LoadBalancerClient 的 默认实现类为 RibbonLoadBalancerClient
我们重点看下这个不带服务实例的execute 是如何 找到合适的 ServiceInstance
有一点可以明确,不带ServiceInstance 最后会去调用 带ServiceInstance的execute,上面也有所体现
public RibbonServer(String serviceId, Server server, boolean secure,
Map<String, String> metadata)
this.serviceId = serviceId;
this.server = server;
this.secure = secure;
this.metadata = metadata;
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
throws IOException
//从这里开始的
//通过serviceId 获取 ILoadBalancer
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
Server server = getServer(loadBalancer, hint);
if (server == null)
throw new IllegalStateException("No instances available for " + serviceId);
//RibbonServer 是 ServiceInstance 的子类
//public static class RibbonServer implements ServiceInstance
RibbonServer ribbonServer = new RibbonServer(serviceId, server,
isSecure(server, serviceId),
serverIntrospector(serviceId).getMetadata(server));
return execute(serviceId, ribbonServer, request);
@Override
public <T> T execute(String serviceId, ServiceInstance serviceInstance,
LoadBalancerRequest<T> request) throws IOException
Server server = null;
if (serviceInstance instanceof RibbonServer)
server = ((RibbonServer) serviceInstance).getServer();
通过serviceId 获取 ILoadBalancer
protected ILoadBalancer getLoadBalancer(String serviceId)
return this.clientFactory.getLoadBalancer(serviceId);
serviceId 也就是服务名
public ILoadBalancer getLoadBalancer(String name)
return getInstance(name, ILoadBalancer.class);
@Override
public <C> C getInstance(String name, Class<C> type)
C instance = super.getInstance(name, type);
if (instance != null)
return instance;
IClientConfig config = getInstance(name, IClientConfig.class);
return instantiateWithConfig(getContext(name), type, config);
根据loadBalancer和hint(null)获取Service
因为上面hint为null,这里变成了default
protected Server getServer(ILoadBalancer loadBalancer, Object hint)
if (loadBalancer == null)
return null;
// Use 'default' on a null hint, or just pass it on?
return loadBalancer.chooseServer(hint != null ? hint : "default");
public Server chooseServer(Object key);
接着到BaseLoadBalancer
public Server chooseServer(Object key)
if (counter == null)
counter = createCounter();
counter.increment();
if (rule == null)
return null;
else
try
//这里根据key进行选择
return rule.choose(key);
catch (Exception e)
logger.warn("LoadBalancer []: Error choosing server for key ", name, key, e);
return null;
public Server choose(Object key);
跟到这里我们就可以看到熟悉的负载均衡策略了,包括我们自定义的。
下面的就是具体的策略选择不同的服务了。
使用自定义规则
ConfigBean
package com.keafmd.springcloud.config;
import com.keafmd.myrule.MyRandomRule;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import com.netflix.loadbalancer.WeightedResponseTimeRule;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* Keafmd
*
* @ClassName: ConfigBean
* @Description:
* @author: 牛哄哄的柯南
* @date: 2022-07-06 17:48
*/
@Configuration
public class ConfigBean
@LoadBalanced //配置负载均衡实现RestTemplate
@Bean
public RestTemplate getRestTemplate()
return new RestTemplate();
/**
* IRule:
* RoundRobinRule 轮询策略
* RandomRule 随机策略
* AvailabilityFilteringRule : 会先过滤掉,跳闸,访问故障的服务~,对剩下的进行轮询~
* RetryRule : 会先按照轮询获取服务~,如果服务获取失败,则会在指定的时间内进行,重试
*/
@Bean //注释掉 使用自定义规则
public IRule myRule()
return new MyRandomRule();
// return new RandomRule();//使用随机策略
//return new RoundRobinRule();//使用轮询策略
//return new AvailabilityFilteringRule();
//return new RetryRule();
//return new WeightedResponseTimeRule();
MyRandomRule
继承 AbstractLoadBalancerRule
package com.keafmd.myrule;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
/**
* Keafmd
*
* @ClassName: MyRandomRule
* @Description:
* @author: 牛哄哄的柯南
* @date: 2022-07-07 14:58
*/
public class MyRandomRule extends AbstractLoadBalancerRule
/**
* 每个服务访问5次则换下一个服务(总共3个服务)
* <p>
* total=0,默认=0,如果=5,指向下一个服务节点
* index=0,默认=0,如果total=5,index+1
*/
private int total = 0;//被调用的次数
private int currentIndex = 0;//当前是谁在提供服务
//@edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE")
public Server choose(ILoadBalancer lb, Object key)
if (lb == null)
return null;
Server server = null;
while (server == null)
if (Thread.interrupted())
return null;
List<Server> upList = lb.getReachableServers();//获得当前活着的服务
List<Server> allList = lb.getAllServers();//获取所有的服务
int serverCount = allList.size();
if (serverCount == 0)
/*
* No servers. End regardless of pass, because subsequent passes
* only get more restrictive.
*/
return null;
//int index = chooseRandomInt(serverCount);//生成区间随机数
//server = upList.get(index);//从或活着的服务中,随机获取一个
//=====================自定义代码=========================
if (total < 5)
server = upList.get(currentIndex);
total++;
else
total = 0;
currentIndex++;
if (currentIndex > upList.size())
currentIndex = 0;
server = upList.get(currentIndex);//从活着的服务中,获取指定的服务来进行操作
//======================================================
if (server == null)
/*
* The only time this should happ以上是关于负载均衡原理,探究@LoadBalanced注解都做了什么(Ribbon)的主要内容,如果未能解决你的问题,请参考以下文章
负载均衡原理,探究@LoadBalanced注解都做了什么(Ribbon)