软件定制开发供应商负载均衡原理,探究@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中有组件来负责负载均衡。spring的负载均衡需要用到服务注册中心eruka。
服务提供方:将自己注册到服务注册中心eruka
服务消费方:从服务注册中心中获取服务列表,使用服务
客户端的负载均衡流程如下:
服务消费方通过ribbon先从服务注册中心获取服务列表,根据一定的负载均衡算法,分发请求到不同的服务提供方
客户端从服务中心选择一个,去调用
参考自:http://t.csdn.cn/zBP3x
常见的负载均衡算法
当然,我们可以去自定义负载均衡算法。
Ribbon负载均衡组件
Ribbon(spring-cloud-starter-ribbon)
我们都知道使用RestTemplate
时,可以直接使用服务名进行服务调用,只需要在定义RestTemplate
时加上@LoadBalanced
注解就可以了
@LoadBalanced //配置负载均衡实现RestTemplate@Beanpublic RestTemplate getRestTemplate(){ return new RestTemplate();}
- 1
- 2
- 3
- 4
- 5
接下来,我们重点分析下@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); } } }); }; } ...}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
接着看这个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); }; }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
这里就是 @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)); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
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);}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
public interface LoadBalancerRequest<T> { T apply(ServiceInstance instance) throws Exception;}
- 1
- 2
- 3
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); }; }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
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; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
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(); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
通过serviceId 获取 ILoadBalancer
protected ILoadBalancer getLoadBalancer(String serviceId) { return this.clientFactory.getLoadBalancer(serviceId); }
- 1
- 2
- 3
serviceId 也就是服务名
public ILoadBalancer getLoadBalancer(String name) { return getInstance(name, ILoadBalancer.class); }
- 1
- 2
- 3
@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); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
根据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"); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
public Server chooseServer(Object key);
- 1
接着到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; } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
public Server choose(Object key);
- 1
跟到这里我们就可以看到熟悉的负载均衡策略了,包括我们自定义的。
下面的就是具体的策略选择不同的服务了。
使用自定义规则
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 */@Configurationpublic 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(); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
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 happen is if the server list were * somehow trimmed. This is a transient condition. Retry after * yielding. */ Thread.yield(); continue; } if (server.isAlive()) { return (server); } // Shouldn't actually happen.. but must be transient or a bug. server = null; Thread.yield(); } return server; } protected int chooseRandomInt(int serverCount) { return ThreadLocalRandom.current().nextInt(serverCount); } @Override public Server choose(Object key) { return choose(getLoadBalancer(), key); } @Override public void initWithNiwsConfig(IClientConfig clientConfig) { // TODO Auto-generated method stub }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
版权声明:
原创博主:牛哄哄的柯南
博主原文链接:
个人博客链接:
看完如果对你有帮助,感谢点击下面的点赞支持!
[哈哈][抱拳]
加油!
共同努力!
Keafmd