前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >基于开源Tars的动态负载均衡实践

基于开源Tars的动态负载均衡实践

原创
作者头像
2020labs小助手
修改于 2021-06-03 02:13:33
修改于 2021-06-03 02:13:33
1.4K00
代码可运行
举报
文章被收录于专栏:vivo互联网技术vivo互联网技术
运行总次数:0
代码可运行

一、背景

vivo 互联网领域的部分业务在微服务的实践过程当中基于很多综合因素的考虑选择了TARS微服务框架。

官方的描述是:TARS是一个支持多语言、内嵌服务治理功能,与Devops能很好协同的微服务框架。我们在开源的基础上做了很多适配内部系统的事情,比如与CICD构建发布系统、单点登录系统的打通,但不是这次我们要介绍的重点。这里想着重介绍一下我们在现有的负载均衡算法之外实现的动态负载均衡算法。

二、什么是负载均衡

维基百科的定义如下:负载平衡(Load balancing)是一种电子计算机技术,用来在多个计算机(计算机集群)、网络连接、CPU、磁盘驱动器或其他资源中分配负载,以达到优化资源使用、最大化吞吐率、最小化响应时间、同时避免过载的目的。使用带有负载平衡的多个服务器组件,取代单一的组件,可以通过冗余提高可靠性。负载平衡服务通常是由专用软件和硬件来完成。主要作用是将大量作业合理地分摊到多个操作单元上进行执行,用于解决互联网架构中的高并发和高可用的问题。

这段话很好理解,本质上是一种解决分布式服务应对大量并发请求时流量分配问题的方法。

三、TARS 支持哪些负载均衡算法

TARS支持三种负载均衡算法,基于轮询的负载均衡算法、基于权重分配的轮询负载均衡算法、一致性hash负载均衡算法。函数入口是selectAdapterProxy,代码在 TarsCpp 文件里,感兴趣的可以从这个函数开始深入了解。

3.1 基于轮询的负载均衡算法

基于轮询的负载均衡算法实现很简单,原理就是将所有提供服务的可用 ip 形成一个调用列表。当有请求到来时将请求按时间顺序逐个分配给请求列表中的每个机器,如果分配到了最后列表中的最后一个节点则再从列表第一个节点重新开始循环。这样就达到了流量分散的目的,尽可能的平衡每一台机器的负载,提高机器的使用效率。这个算法基本上能满足大量的分布式场景了,这也是TARS默认的负载均衡算法。

但是如果每个节点的处理能力不一样呢?虽然流量是均分的,但是由于中间有处理能力较弱的节点,这些节点仍然存在过载的可能性。于是我们就有了下面这种负载均衡算法。

3.2 基于权重分配的轮询负载均衡算法

权重分配顾名思义就是给每个节点赋值一个固定的权重,这个权重表示每个节点可以分配到流量的概率。举个例子,有5个节点,配置的权重分别是4,1,1,1,3,如果有100个请求过来,则对应分配到的流量也分别是40,10,10,10,30。这样就实现了按配置的权重来分配客户端的请求了。这里有个细节需要注意一下,在实现加权轮询的时候一定要是平滑的。也就是说假如有10个请求,不能前4次都落在第1个节点上。

业界已经有了很多平滑加权轮询的算法,感兴趣的读者可以自行搜索了解。

3.3 一致性Hash

很多时候在一些存在缓存的业务场景中,我们除了对流量平均分配有需求,同时也对同一个客户端请求应该尽可能落在同一个节点上有需求。

假设有这样一种场景,某业务有1000万用户,每个用户有一个标识id和一组用户信息。用户标识id和用户信息是一一对应的关系,这个映射关系存在于DB中,并且其它所有模块需要去查询这个映射关系并从中获取一些必要的用户字段信息。在大并发的场景下,直接请求DB系统肯定是抗不住的,于是我们自然就想到用缓存的方案去解决。是每个节点都需要去存储全量的用户信息么?虽然可以,但不是最佳方案,万一用户规模从1000万上升到1亿呢?很显然这种解决方案随着用户规模的上升,变得捉襟见肘,很快就会出现瓶颈甚至无法满足需求。于是就需要一致性hash算法来解决这个问题。一致性hash算法提供了相同输入下请求尽可能落在同一个节点的保证。

为什么说是尽可能?因为节点会出现故障下线,也有可能因为扩容而新增,一致性hash算法是能够在这种变化的情况下做到尽量减少缓存重建的。TARS使用的hash算法有两种,一种是对key求md5值后,取地址偏移做异或操作,另一种是ketama hash。

四、为什么需要动态负载均衡?

我们目前的服务大部分还是跑在以虚拟机为主的机器上,因此混合部署(一个节点部署多个服务)是常见现象。在混合部署的情况下,如果一个服务代码有bug了占用大量的CPU或内存,那么必然跟他一起部署的服务都会受到影响。

那么如果仍然采用上述三种负载均衡算法的情况下,就有问题了,被影响的机器仍然会按指定的规则分配到流量。也许有人会想,基于权重的轮询负载均衡算法不是可以配置有问题的节点为低权重然后分配到很少的流量么?确实可以,但是这种方法往往处理不及时,如果是发生在半夜呢?并且在故障解除后需要再手动配置回去,增加了运维成本。因此我们需要一种动态调整的负载均衡算法来自动调整流量的分配,尽可能的保证这种异常情况下的服务质量。

从这里我们也不难看出,要实现动态负载均衡功能的核心其实只需要根据服务的负载动态的调整不同节点的权重就可以了。这其实也是业界常用的一些做法,都是通过周期性地获取服务器状态信息,动态地计算出当前每台服务器应具有的权值。

五、动态负载均衡策略

在这里我们采用的也是基于各种负载因子的方式对可用节点动态计算权重,将权重返回后复用TARS静态权重节点选择算法。我们选择的负载因子有:接口5分钟平均耗时/接口5分钟超时率/接口5分钟异常率/CPU负载/内存使用率/网卡负载。负载因子支持动态扩展。

整体功能图如下:

5.1 整体交互时序图

rpc调用时,EndpointManager定期获得可用节点集合。节点附带权重信息。业务在发起调用时根据业务方指定的负载均衡算法选择对应的节点;

RegistrServer定期从db/监控中习获取超时率和平均耗时等信息。从其它平台(比如CMDB)获得机器负载类信息,比如cpu/内存等。所有计算过程线程异步执行缓存在本地;

EndpointManager根据获得的权重执行选择策略。下图为节点权重变化对请求流量分配的影响:

5.2 节点更新和负载均衡策略

节点所有性能数据每60秒更新一次,使用线程定时更新;

计算所有节点权重值和取值范围,存入内存缓存;

主调获取到节点权重信息后执行当前静态权重负载均衡算法选择节点;

兜底策略:如果所有节点要重都一样或者异常则默认采用轮询的方式选择节点;

5.3 负载的计算方式

负载计算方式:每个负载因子设定权重值和对应的重要程度(按百分比表示),根据具体的重要程度调整设置,最后会根据所有负载因子算出的权重值乘对应的百分比后算出总值。比如:耗时权重为10,超时率权重为20,对应的重要程度分别为40%和60%,则总和为10 * 0.4 + 20 * 0.6 = 16。对应每个负载因子计算的方式如下(当前我们只使用了平均耗时和超时率这两个负载因子,这也是最容易在TARS当前系统中能获取到的数据):

1、按每台机器在总耗时的占比反比例分配权重:权重 = 初始权重 *(耗时总和 - 单台机器平均耗时)/ 耗时总和(不足之处在于并不完全是按耗时比分配流量);

2、超时率权重:超时率权重 = 初始权重 - 超时率 * 初始权重 * 90%,折算90%是因为100%超时时也可能是因为流量过大导致的,保留小流量试探请求;

对应代码实现如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void LoadBalanceThread::calculateWeight(LoadCache &loadCache)
{
    for (auto &loadPair : loadCache)
    {
        ostringstream log;
        const auto ITEM_SIZE(static_cast<int>(loadPair.second.vtBalanceItem.size()));
        int aveTime(loadPair.second.aveTimeSum / ITEM_SIZE);
        log << "aveTime: " << aveTime << "|"
            << "vtBalanceItem size: " << ITEM_SIZE << "|";
        for (auto &loadInfo : loadPair.second.vtBalanceItem)
        {
            // 按每台机器在总耗时的占比反比例分配权重:权重 = 初始权重 *(耗时总和 - 单台机器平均耗时)/ 耗时总和
            TLOGDEBUG("loadPair.second.aveTimeSum: " << loadPair.second.aveTimeSum << endl);
            int aveTimeWeight(loadPair.second.aveTimeSum ? (DEFAULT_WEIGHT * ITEM_SIZE * (loadPair.second.aveTimeSum - loadInfo.aveTime) / loadPair.second.aveTimeSum) : 0);
            aveTimeWeight = aveTimeWeight <= 0 ? MIN_WEIGHT : aveTimeWeight;
            // 超时率权重:超时率权重 = 初始权重 - 超时率 * 初始权重 * 90%,折算90%是因为100%超时时也可能是因为流量过大导致的,保留小流量试探请求
            int timeoutRateWeight(loadInfo.succCount ? (DEFAULT_WEIGHT - static_cast<int>(loadInfo.timeoutCount * TIMEOUT_WEIGHT_FACTOR / (loadInfo.succCount           
+ loadInfo.timeoutCount))) : (loadInfo.timeoutCount ? MIN_WEIGHT : DEFAULT_WEIGHT));
            // 各类权重乘对应比例后相加求和
            loadInfo.weight = aveTimeWeight * getProportion(TIME_CONSUMING_WEIGHT_PROPORTION) / WEIGHT_PERCENT_UNIT
                              + timeoutRateWeight * getProportion(TIMEOUT_WEIGHT_PROPORTION) / WEIGHT_PERCENT_UNIT ;
 
            log << "aveTimeWeight: " << aveTimeWeight << ", "
                << "timeoutRateWeight: " << timeoutRateWeight << ", "
                << "loadInfo.weight: " << loadInfo.weight << "; ";
        }
 
        TLOGDEBUG(log.str() << "|" << endl);
    }
}

相关代码实现在RegistryServer,代码文件如下图:

核心实现是LoadBalanceThread类,欢迎大家指正。

5.4 使用方式

  1. 在Servant管理处配置-w -v 参数即可支持动态负载均衡,不配置则不启用。

如下图:

  1. 注意:需要全部节点启用才生效,否则rpc框架处发现不同节点采用不同的负载均衡算法则强制将所有节点调整为轮询方式。

六、动态负载均衡适用的场景

如果你的服务是跑在Docker容器上的,那可能不太需要动态负载均衡这个特性。直接使用Docker的调度能力进行服务的自动伸缩,或者在部署上直接将Docker分配的粒度拆小,让服务独占docker就不存在相互影响的问题了。如果服务是混合部署的,并且服务大概率会受到其它服务的影响,比如某个服务直接把cpu占满,那建议开启这个功能。

七、下一步计划

目前的实现中只考虑了平均耗时和超时率两个因子,这能在一定程度上反映服务能力提供情况,但不够完全。因此,未来我们还会考虑加入cpu使用情况这些能更好反映节点负载的指标。以及,在主调方根据返回码来调整权重的一些策略。

最后也欢迎大家与我们讨论交流,一起为TARS开源做贡献。

作者:vivo互联网服务器团队-Yang Minshan

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
常见的负载均衡算法的实现与应用
所谓负载均衡就是将外部发送过来的请求均匀或者根据某种算法分配到对称结构中的某一台服务器中。负载均衡可以分为硬件负载均衡和软件负载均衡,常见的硬件负载均衡有F5、Array等,但是这些设备都比较昂贵。相比之下,利用软件来实现负载均衡就比较简单了,常见的像是 Nginx 的反向代理负载均衡。
烂猪皮
2023/09/03
2850
常见的负载均衡算法的实现与应用
Nginx 负载均衡
负载均衡就是将请求“均衡”地分配到多台业务节点服务器上。这里的“均衡”是依据实际场景和业务需要而定的。
jwangkun
2021/12/23
8780
Nginx 负载均衡
nginx负载均衡的5种策略及原理
1、轮询(默认) 每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。 upstream backserver { server 192.168.0.14; server 192.168.0.15; }
全栈程序员站长
2022/09/07
6.9K0
漫谈负载均衡
负载均衡是什么鬼?从字面意思来看,它应该有两层意思分别是负载和均衡。而对于系统负载均衡它同样具有两层意思,其中系统负载指的系统能够承载的最大访问流量,系统均衡指的是前端请求要均匀地分配给后端机器,同时,同一用户要尽可能分配给同一机器。系统通过负载均衡以后具有如下好处:
木可大大
2018/07/25
4900
漫谈负载均衡
Nginx专题(2):Nginx的负载均衡策略及其配置
前篇Nginx专题(1):Nginx之反向代理及配置详细介绍了Nginx功能之一——反向代理。本篇文章将重点介绍Nginx功能之二——负载均衡。
宜信技术学院
2019/12/05
2.4K0
几种简单的负载均衡算法
什么是负载均衡 负载均衡,英文名称为Load Balance,指由多台服务器以对称的方式组成一个服务器集合,每台服务器都具有等价的地位,都可以单独对外提供服务而无须其他服务器的辅助。通过某种负载分担技术,将外部发送来的请求均匀分配到对称结构中的某一台服务器上,而接收到请求的服务器独立地回应客户的请求。负载均衡能够平均分配客户请求到服务器阵列,借此提供快速获取重要数据,解决大量并发访问服务问题,这种集群技术可以用最少的投资获得接近于大型主机的性能。 负载均衡分为软件负载均衡和硬件负载均衡,前者的代表是
Spark学习技巧
2018/04/17
2K0
几种简单的负载均衡算法
负载均衡算法
缺点:没有考虑机器的性能问题,根据木桶最短木板理论,集群性能瓶颈更多的会受性能差的服务器影响。
才疏学浅的木子
2023/10/17
2240
nginx之负载均衡
主要使用的是nginx的http模块来实现两台或多台http服务器的负载均衡,配置为:
山行AI
2019/07/16
1.4K0
nginx之负载均衡
Nginx负载均衡简单配置
严格的说,Nginx仅仅是作为Nginx Proxy反向代理使用的。普通的负载均衡软件,例如LVS,其实现的功能只是对请求数据包的转发(也可能会改写数据包)、传递,其中DR模式明显的特征是从负载均衡下面的节点服务器来看,接收到的请求还是来自访问负载均衡的客户端的真实用户。而反向代理不一样,反向代理接收访问用户的请求后,会代理用户重新发起请求代理下的节点服务器,最后把数据返回给客户端用,在节点服务器看来,访问的节点服务器的客户端就是反向代理服务器了,而非真是的网络用户。
双面人
2019/10/24
8710
Dubbo负载均衡
接口LoadBalance 的定义说明,LoadBalance 的实现只是在一个服务提供的调用者列表(invokers)中选出一个调用者即可,默认的负载方式是随机负载均衡(@SPI(RandomLoadBalance.NAME)),我们也可以指定使用哪种负载均衡:
用户5325874
2020/01/16
1.1K0
多种负载均衡算法及其 Java 代码实现
负载均衡 建立在现有网络结构之上,它提供了一种廉价有效透明的方法扩展 网络设备和 服务器的带宽、增加 吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性。
哲洛不闹
2018/09/14
9090
多种负载均衡算法及其 Java 代码实现
Dubbo负载均衡策略实现[通俗易懂]
1.根据@SPI(RandomLoadBalance.NAME)注解可以知道dubbo的默认负载均衡策略是RandomLoadBalance。
全栈程序员站长
2022/09/08
6760
Dubbo负载均衡策略实现[通俗易懂]
如何选型微服务架构的负载均衡算法?
假设你订阅了一个别人的服务,从注册中心查询得到了这个服务的可用节点列表,而这个列表里包含了几十个节点,这个时候你该选择哪个节点发起调用呢?这就是客户端负载均衡算法的问题。
JavaEdge
2022/11/30
3690
如何选型微服务架构的负载均衡算法?
TAF 必修课(七):负载均衡
作者:温昂展 前面在过载保护章节中已提及了负载均衡,顾名思义,本节要探究的对象为负载/请求。负载均衡除了起到过载预防的作用,本质上是提高了系统的吞吐量,最小化响应时间,到达资源利用最大化。 一、算法与
serena
2017/09/12
1.8K0
Dubbo负载均衡解析
相比Ribbon负载均衡策略里的十八般兵器,Dubbo就显得低调的多了,它只提供了负载均衡四件套,让我们先来简单了解一下:
程序员波特
2024/01/19
1990
Dubbo负载均衡解析
16-Nginx负载均衡策略详解
默认的负载均衡策略, 常用于多台服务器,资源配置一样的情况, 这样可以把流量均匀的分配到每台服务器
彼岸舞
2022/09/13
4260
16-Nginx负载均衡策略详解
什么是负载均衡?
IT派 - {技术青年圈} 持续关注互联网、大数据、人工智能领域 来源:xybaby 链接: http://www.cnblogs.com/xybaby/p/7867735.html 古人云,不患寡而患不均。 在计算机的世界,这就是大家耳熟能详的负载均衡(load balancing),所谓负载均衡,就是说如果一组计算机节点(或者一组进程)提供相同的(同质的)服务,那么对服务的请求就应该均匀的分摊到这些节点上。负载均衡的前提一定是“provide a single Intern
IT派
2018/03/28
1.1K0
什么是负载均衡?
浅析负载均衡的6种算法,Ngnix的5种算法
常见的几种负载均衡算法 1、轮询法 将请求按顺序轮流地分配到后端服务器上,它均衡地对待后端的每一台服务器,而不关心服务器实际的连接数和当前的系统负载。 2、随机法 通过系统的随机算法,根据后端服务器的列表大小值来随机选取其中的一台服务器进行访问。由概率统计理论可以得知,随着客户端调用服务端的次数增多, 其实际效果越来越接近于平均分配调用量到后端的每一台服务器,也就是轮询的结果。 3、源地址哈希法 源地址哈希的思想是根据获取客户端的IP地址,通过哈希函数计算得到的一个数值,用该数值对服务器列表的大小进行取
企鹅号小编
2018/01/15
1.6K0
浅析负载均衡的6种算法,Ngnix的5种算法
dubbo负载均衡策略和集群容错机制
默认情况下,dubbo是random load balance随机调用实现负载均衡,可以对provider不同实例设置不同的权重,会按照权重来负载均衡,权重越大分配流量越高,一般就用这个默认的就可以了。
全栈程序员站长
2022/09/14
3480
Dubbo负载均衡、动态代理策略详解
这些都是关于Dubbo必须知道,基本原理,序列化是什么协议,具体用dubbo的时候,如何负载均衡,如何高可用,如何动态代理等.
JavaEdge
2019/07/08
1.2K0
Dubbo负载均衡、动态代理策略详解
相关推荐
常见的负载均衡算法的实现与应用
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验