软件定制开发供应商负载均衡原理,探究@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中有组件来负责负载均衡。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

网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发