nginx swrr负载均衡算法的二宗罪及其改进的思考

news2024/11/24 4:06:44

目录

  • 1. swrr负载均衡算法的二宗罪
    • 1.1 第一宗罪: 共振引起系统崩溃
    • 1.2 第二宗罪: 吃CPU大户
  • 2. 对swrr负载均衡算法的改进的思考
    • 2.1 “共振”问题的解决
    • 2.2 “吃CPU大户”问题的解决

1. swrr负载均衡算法的二宗罪

   swrr是一种基于加权轮询的负载均衡算法。它根据服务器的权重来分配请求。具体而言,swrr维护一个服务器列表,每个服务器都有一个权重值,表示其处理请求的能力。当一个请求到达时,swrr会按照服务器的权重顺序依次将请求转发到服务器上,然后更新服务器的权重。这样可以实现将请求均匀地分配给具有不同处理能力的服务器。关于swrr的实现原理可以参考我的博文《深入理解nginx负载均衡round-robin算法》。

   nginx的官方原生的round-robin功能就是基于swrr算法来实现的。就如其名字swrr表明的,Smooth Weighted round-robin,它比最简单的round-robin功能有了重要的改进,它不是简单的依次轮询,而是按照服务器权重平滑分配的能力,一定程度上避免了rs服务器的突发峰值的问题。

  本文要细数一下swrr的两宗罪:

1.1 第一宗罪: 共振引起系统崩溃

  我们先来看看阿里的真实案例。《阿里七层流量入口负载均衡算法演变之路》中提到的一个压测案例,当一个机房内的接入层tengine统一将某个rs服务器的权重从1改成2以后,被调高权重rs服务器的流量瞬间冲高了50倍左右,并且持续了7s左右的时间后QPS开始下降,流量才逐渐恢复到预期的值。

  该文中给出了相关现象的详细描述,但是具体什么原因却没有详细说明。这里就来分析一下。对于swrr,如果按照nginx单个进程来说,其实它已经做得足够好了,顾名思义,它已经是平滑的了。但是,如果我们放在一个拥有32核心,甚至64核心或者更多的服务器上,那么我们开启的nginx worker进程就是32,或者64个,如果再像阿里的压测环境,可能又有几十台作为负载均衡功能的nginx前端接入服务器,那么可能就是并发运行的nginx进程就有几百个甚至上千个。swrr在每个进程中都会被初始化为一个状态,如果有三个rs server分别为A、B、C,并假设通过swrr算法,但个进程依次分配rs得到以下序列 B、C、A、B、A、C…

  另外,假设总共有100个nginx worker进程在运行,我们可以想象到,第一个请求进来的时候,因为所有的nginx进程分配的第一个rs必然是B,因此第一个请求分配到的rs是B的概率是100%,那么第二个请求进来的时候,因为有99个进程将分配B,而之前分配了B的服务器将分配C,那么分配到B的概率将是99%,依次类推,第三个请求分配到B的概率也97%,这就导致了B服务器的压力在短时间内会飚得特别高,就像《阿里七层流量入口负载均衡算法演变之路》中给出的那个图像一样:

在这里插入图片描述

  所以,关键的问题在于每个nginx的worker进程因为每个进程都步调一致,导致了“共振现象”,引起了部分rs服务器的流量突发性增高,和原本的预期背道而驰了。我们回顾一下中学物理上面介绍的一个经典的军队用统一步伐过桥最终引起大桥垮塌的案例就可以与这个案例引起共鸣,一言以蔽之,swrr引起的流量突发也是因为“共振”引起的流量突发。

  当然,随着时间的推移,最终每个进程由于分配到的请求的随机性因素,由于swrr的平滑负载均衡能力,必然会慢慢达到预期的负载效果,但是,我们要注意的是,前提是因为流量突发,这些rs服务器都能够扛过流量洪峰的袭击才可以,而往往可能因为流量洪峰过来的时候,部分rs服务器服务器早就挂掉了,导致引起整个系统的雪崩,根本来不及达到最终的预期的负载均衡效果。

1.2 第二宗罪: 吃CPU大户

  swrr算法在我们平常的小规模的负载均衡系统中,譬如rs服务器只有几台,最多10几台的情况下,其实是可以运行得很好的,所以我们一般也不会碰到这种问题。但是,放在一个大规模的集群环境中,就像《 QPS 比 Nginx 提升 60%,阿里 Tengine 负载均衡算法揭秘》提到的那样,如果upstream中设置的rs服务器有2000台,swrr的“吃CPU大户”的弊端就会暴露无遗了。我们先来看看swrr算法的代码,以下是我参照nginx的round-robin代码写的一段c代码:

// effective_weight 三台rs服务器,设置了每台rs服务器对应的权重
int ew[] = {1,2,4};           

#define PN   sizeof(ew)/sizeof(int)

// current_weight 对应三台rs服务器的计算出来的本轮权重 
int cw[PN] = {0};

int rr()
{
	int i;

	int ttl = 0;

	int cur_w = -1;
	int cur = -1;

	for (i=0; i < PN; i++) {
		cw[i] += ew[i];
		ttl += ew[i];

		if (cur_w < cw[i]) {
			cur = i;
			cur_w = cw[i];
		}
	}

	cw[cur] -= ttl;
	
	return cur;
}

   具体算法的逻辑就不再这里介绍了,可以参考《 QPS 比 Nginx 提升 60%,阿里 Tengine 负载均衡算法揭秘》。需要注意的一点是,swrr算法每次进行负载均衡选择一台rs服务器,都需要进行一次完整的遍历,有2000台rs那么就需要循环2000次,这就是问题的所在。就像上面说的那样,对于upstream中rs数量少的情况大家可能感受不到,如果有2000台rs的规模,那大量的cpu资源就白白浪费在循环上了,确实和nginx是从头到尾塑造的“高效率”web服务器的形象大相径庭。

2. 对swrr负载均衡算法的改进的思考

2.1 “共振”问题的解决

  在中学物理军队用统一步伐过桥最终引起大桥垮塌的案例中,物理老师告诉我们的解决办法是改齐步走为便步走,便步走就引入了随机性,这样就很好地避免了共振的发生。而我们swrr算法也同样可以通过引入随机性来解决“共振”现象的发生。所以阿里的tengine中引入了vnswrr算法,该算法就是引入了“随机性”,虽然每个nginx进程还是使用swrr算法,但是每个nginx进程引入了一个随机值,在其第一次初始化完首批所谓的“虚拟节点”列表后,每个进程会设置一个随即的起始轮询位置,这样每个进程各自走各自的“便步”,从而避免了“共振”的发生。

 emsp;当然,我们如果不考虑解决CPU消耗的问题,我们完全可以不用虚拟节点的办法,在nginx初始化负载均衡算法加载完成peer列表以后,每个进程随机地取一个[0,N)中的值n, N为peer数量,预先执行n遍swrr算法,从而强行使得各个进程的步调不一致,一样可以解决这个问题。

  当然,如果我们只有少数几台nginx服务器作为负载均衡服务器,那么我们完全不用那么负载,利用nginx官方原生的ngx_http_upstream_zone_module模块也就能很好地解决问题了。关于ngx_http_upstream_zone_module模块的详细说明可以参考《深入理解nginx upstream共享内存机制》。因为ngx_http_upstream_zone_module将peer的分配状态放在共享内存中,每个worker进行负载均衡的时候,会线性地执行swrr算法,swrr算法的当前状态也会更新的共享内存中,还是拿军队过大桥的例子来说,ngx_http_upstream_zone_module模块会限制每个人一个一个过桥,而不是大家一起过桥,从而避免了"共振"问题的发生。

2.2 “吃CPU大户”问题的解决

  解决这个问题最理想的方法就是在负载均衡的时候把循环去掉,把算法的复杂度从O(n)改称O(1)。如果理解了swrr算法,要改起来也不是太复杂,因为swrr算法每Total Weight(即所有服务器分配的权重的总和)次负载均衡请求,就会回到原始状态并重新开始。因此,我们可以提前把分配表建好,后续进行负载均衡的时候,只要对着这个分配表每次往后移动一格就可以了,自然就实现了O(1)的性能。

  不过,tengine的vnswrr算法考虑得更加全面一些,因为如果rs服务器数量非常多的情况下,一次性生成这个分配表的计算量也是不少的,计算量是O(n*w)(w为总的权重)的,如果是两前台服务器,计算量也是相当可观的,所以vnswrr就将这个分配表的生成任务化整为零,每次只生成固定的几个,用完再分配,直到整个分配表填充完毕后就循环利用分配表进行执行负载均衡分配rs服务器就可以了,这样就大大降低了CPU使用率的突发问题,提升了系统的抗压能力。

  从阿里压测给到的数据来看,用wrk 压测工具、500 并发、长连接场景、upstream 配置 2000 个 server的测试条件下。Nginx 官方的 SWRR 和改进的 VNSWRR 算法下的 QPS 处理能力如下图所示:其中 VNSWRR 的 QPS 处理能力相对于 SWRR 算法提升 60% 左右。

  至于vnswrr的详细算法原理,可以参考《阿里七层流量入口负载均衡算法演变之路》](https://www.upyun.com/opentalk/444.html),后续本人也将单独写一篇关于ngx_http_upstream_vnswrr_module模块源码分析的文章,敬请期待。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1512562.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第42期 | GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以找…

力扣L6--- 两数之和(java版)--2024年3月12日

1.题目 2.知识点 注1&#xff1a;在Java中&#xff0c;数组的长度不是通过调用一个方法获得的&#xff0c;而是通过一个属性直接获得的。因此&#xff0c;正确的语法是nums.length而不是nums.length()。 所以应该使用int m nums.length; 注2&#xff1a;return new int[]{i,…

保研复习数据结构记(4)--树(二叉树、线索树、哈夫曼树,并查集)

一.树的基本术语 1.树 什么是空树&#xff1f;结点数为0的树非空树的特性&#xff1f;有且仅有一个根结点&#xff0c;没有后继的结点称为“叶子结点”&#xff0c;有后继的结点称为“分支结点”&#xff0c;除了根结点外任何一个结点都有且仅有一个前驱&#xff0c;每个结点…

JS的对象

目录 对象&#xff1a;object 对象的创建&#xff1a; 利用对象字面量创建对象&#xff1a; 使用new来进行创建对象&#xff1a; 利用构造函数来创建对象&#xff1a; new的执行&#xff1a; 对象属性的遍历&#xff1a;for in ------ 相当于JAVA的工具类&#xff0c;直…

【NR 定位】3GPP NR Positioning 5G定位标准解读(十四)-DL-TDOA 定位

前言 3GPP NR Positioning 5G定位标准&#xff1a;3GPP TS 38.305 V18 3GPP 标准网址&#xff1a;Directory Listing /ftp/ 【NR 定位】3GPP NR Positioning 5G定位标准解读&#xff08;一&#xff09;-CSDN博客 【NR 定位】3GPP NR Positioning 5G定位标准解读&#xff08;…

git pull 报错: 在签出前,请清理存储库工作树

问题&#xff1a; 使用vscode 用git 拉取代码&#xff0c;提示&#xff1a;在签出前&#xff0c;请清理存储库工作树** 原因&#xff1a; git仓库上的代码和本地代码存在冲突了所以会报这个报错。 解决办法&#xff1a; ①git stash 先将本地修改存储起来 ②git pull 拉取远…

websocket 使用示例

websocket 使用示例 前言html中使用vue3中使用1、安装websocket依赖2、代码 vue2中使用1、安装websocket依赖2、代码 前言 即时通讯webSocket 的使用 html中使用 以下是一个简单的 HTML 页面示例&#xff0c;它连接到 WebSocket 服务器并包含一个文本框、一个发送按钮以及 …

案例--某站视频爬取

众所周知&#xff0c;某站的视频是&#xff1a; 由视频和音频分开的。 所以我们进行获取&#xff0c;需要分别获得它的音频和视频数据&#xff0c;然后进行音视频合并。 这么多年了&#xff0c;某站还是老样子&#xff0c;只要加个防盗链就能绕过。&#xff08;防止403&#xf…

第十四届蓝桥杯C++B组编程题题目以及题解

a.冶炼金属&#xff08;二分&#xff09; 思路&#xff1a; 设任意一条冶炼记录投入金属数量为a,产出金属为b. 对于每一条冶炼记录我们都可以得到 一个转换率V的范围&#xff1a; b<a/v<b1即a/b< v <a/(b1) 为什么是b1呢&#xff1f;因为既然能产出b个金属&#xf…

SpringCloud网关路由及实现

目录 1 前言 2 实现步骤 2.1 创建一个模块作为网关并引入相关依赖 2.3 设置启动类 2.4 配置路由 3 网关路由的补充内容 3.1 路由断言 3.2 路由过滤器 1 前言 前端请求不能直接访问微服务&#xff0c;而是要请求网关。原因及网关的作用如下&#xff1a; ①网关做安全控制…

16. UE5 RPG获取GE应用的回调,并根据Tag设置数据显示到窗口

在上一篇介绍了对标签如何在项目中设置&#xff0c;这一篇先讲解一下如何在GE里面使用GameplayTag标签。 之前我在第十一章节中 11. UE5 RPG使用GameplayEffect修改角色属性&#xff08;二&#xff09;介绍了一些GE的属性&#xff0c;在UE 5.3版本中&#xff0c;修改的配置方式…

Docker部署黑马商城项目笔记

部署后端 创建mysql目录如下&#xff0c;上传对应的文件 运行以下命令 docker run -d \--name mysql \-p 3306:3306 \-e TZAsia/Shanghai \-e MYSQL_ROOT_PASSWORD123 \-v ./mysql/data:/var/lib/mysql \-v ./mysql/conf:/etc/mysql/conf.d \-v ./mysql/init:/docker-entry…

LeetCode(力扣)算法题_1261_在受污染的二叉树中查找元素

今天是2024年3月12日&#xff0c;可能是因为今天是植树节的原因&#xff0c;今天的每日一题是二叉树&#x1f64f;&#x1f3fb; 在受污染的二叉树中查找元素 题目描述 给出一个满足下述规则的二叉树&#xff1a; root.val 0 如果 treeNode.val x 且 treeNode.left ! n…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的常见手势识别系统(深度学习模型+UI界面代码+训练数据集)

摘要&#xff1a;开发手势识别系统对于增强人机交互和智能家居控制领域的体验非常关键。本博客详尽阐述了通过深度学习技术构建手势识别系统的过程&#xff0c;并附上了全套实施代码。系统采用了先进的YOLOv8算法&#xff0c;并通过与YOLOv7、YOLOv6、YOLOv5的性能对比&#xf…

PTA- - -个位数统计(C语言)

Hello,好久没更新啦&#xff0c;今天给大家讲解一下PTA平台上面的“个位数统计”这道题吧~ 题目是要统计一个数字每个位上数字出现的次数。下面是一个解决方案的思路和相应的 C 语言代码&#xff1a; 思路&#xff1a; 初始化一个大小为10的数组&#xff0c;用于计数每个数字…

Kubernetes(k8s第四部分之servers)

1&#xff0c;为什么不使用round-robin DNS&#xff1f; 因为DNS有缓存&#xff0c;不会清理&#xff0c;无法负载均衡 ipvs代理模式&#xff0c;这种模式&#xff0c;kube-proxy会监视Kubernetes Service 对象和Endpoints&#xff0c;调用netlink接口以相应地创建ipvs规则并…

小迪安全39WEB 攻防-通用漏洞CSRFSSRF协议玩法内网探针漏洞利用

#知识点&#xff1a; 逻辑漏洞 1、CSRF-原理&危害&探针&利用等 2、SSRF-原理&危害&探针&利用等 3、CSRF&SSRF-黑盒下漏洞探针点 #详细点&#xff1a; CSRF 全称&#xff1a;Cross-site request forgery&#xff0c;即&#xff0c;跨站请求…

十四、软考-系统架构设计师笔记-云原生架构设计理论与实践

1、云原生架构背景 云原生架构定义 从技术的角度&#xff0c;云原生架构是基于云原生技术的一组架构原则和设计模式的集合&#xff0c;旨在将云应用中的非业务代码部分进行最大化的剥离&#xff0c;从而让云设施接管应用中原有的大量非功能特性(如弹性、韧性、安全、可观测性、…

图片怎样去水印?三款热门工具推荐!

在数字化时代&#xff0c;图片去水印成为了许多设计师、摄影师和普通用户的基本需求。面对市面上琳琅满目的去水印工具&#xff0c;究竟哪款应用能够在效果、易用性和效率上更胜一筹呢&#xff1f;今天&#xff0c;就让我们来对比三款国内外热门的图片去水印应用&#xff0c;看…

基于pci多功能采集卡——pci9640

一、追逐潮流&#xff0c;应运而生 信息社会的高速发展&#xff0c;在很大程度上取决于信息与信号处理的先进性。数字信号处理技术的出现改变了信号与信号处理技术的整个面貌&#xff0c;而数据采集作为数字信号处理的必不可少的前期工作在整个数字系统中起到关键性乃至决定性的…