首页 > 编程语言 >IPVS(LVS)中的调度算法详解(一)

IPVS(LVS)中的调度算法详解(一)

时间:2024-12-14 22:10:38浏览次数:5  
标签:rr LVS dest ip vs 调度 IPVS 详解 服务器

一、IPVS中的算法类型

IP Virtual Server version 1.2.1
kernel 6.6.0-28.0.0.34.oe2403.x86_64
 --scheduler    -s scheduler         one of rr|wrr|lc|wlc|lblc|lblcr|dh|sh|sed|nq|fo|ovf|mh,

当前版本的LVS共有rr|wrr|lc|wlc|lblc|lblcr|dh|sh|sed|nq|fo|ovf|mh,共13种算法。下面我们对这集中算法进行详细介绍。

二、IPVS中的算法详解

1.轮询(Round - Robin,RR)算法

  1. 基本原理

    • RR(Round - Robin)即轮询算法,是一种简单且直观的负载均衡算法。其核心原理是按照顺序依次将客户端的请求分配到后端的真实服务器(Real Server)上。
    • 假设在一个LVS负载均衡集群中有n台真实服务器,分别标记为RS1、RS2、...、RSn。当第一个客户端请求到达负载均衡器(LVS)时,LVS会将这个请求分配给RS1;当第二个客户端请求到来时,分配给RS2;以此类推,当第n个客户端请求到来时,分配给RSn。当第n + 1个客户端请求到来时,又重新从RS1开始分配,如此循环往复。
    • 这种分配方式就像排队按顺序领东西一样,每个服务器都有均等的机会接收请求,使得请求在各个服务器之间均匀分布。
  2. 实现过程

    • 维护服务器列表
      • LVS会维护一个包含所有真实服务器的列表。这个列表记录了服务器的相关信息,如服务器的IP地址、端口号等。在轮询过程中,LVS会根据这个列表的顺序来分配请求。
    • 请求分配循环
      • 当有新的客户端请求到达时,LVS会查看当前应该分配到列表中的哪一台服务器。它通过一个内部的计数器来跟踪当前分配到的服务器位置。每次分配请求后,计数器会增加1,当计数器的值超过服务器列表的大小(即n)时,计数器会重新归零,从而开始新的一轮分配。
    • 连接处理
      • LVS在将请求分配给真实服务器后,会建立起客户端与该真实服务器之间的连接。对于基于TCP/IP协议的请求,LVS会处理好连接的转发工作,使得客户端和真实服务器之间能够顺利通信。例如,在TCP的三次握手过程中,LVS会协调客户端和真实服务器完成握手,之后将客户端发送的数据转发给真实服务器,并将真实服务器返回的数据转发给客户端。
  3. 特点

    • 简单公平
      • RR算法的最大优点是简单易懂且公平。每个真实服务器都有相同的机会处理客户端请求,这在服务器性能相近的情况下能够很好地平衡各服务器的负载。例如,在一个由多台相同配置的Web服务器组成的集群中,使用RR算法可以确保每台服务器大致处理相同数量的HTTP请求。
    • 不考虑服务器性能差异
      • 然而,RR算法的缺点也很明显,它没有考虑服务器之间的性能差异。如果集群中的服务器性能不同,比如有的服务器处理能力强,有的服务器处理能力弱,使用RR算法可能会导致性能强的服务器资源得不到充分利用,而性能弱的服务器可能会因为分配到过多的请求而过载。例如,一台服务器的CPU处理速度是另一台的两倍,但RR算法依然会平均分配请求,这可能会导致较慢的服务器响应时间变长,甚至出现故障。
    • 缺乏动态适应性
      • 另外,RR算法是一种静态的分配方式,它不会根据服务器的当前负载状态(如连接数、CPU使用率、内存使用率等)来动态调整请求分配。即使某台服务器已经负载过重,只要按照轮询顺序轮到它,还是会继续分配请求给它。这在实际的复杂应用场景中可能会影响系统的整体性能和稳定性。
  4. 适用场景

    • 服务器性能相近的集群
      • RR算法适用于后端真实服务器性能基本相同的负载均衡场景。例如,在一个小型的Web服务器集群中,如果所有服务器的硬件配置(CPU、内存、网络带宽等)相同,并且运行相同的软件环境,RR算法可以简单有效地将客户端的HTTP请求均匀分配到各个服务器上,实现基本的负载均衡,从而提高整个集群的处理能力和可用性。
      • 另外,在一些对负载均衡精度要求不高,且服务器性能较为均衡的测试环境或者开发环境中,RR算法也可以作为一种简单的负载均衡方案来使用。

linux源码中的实现

//源码位置:net/netfilter/ipvs/ip_vs_rr.c
/**
 * @brief 轮询调度算法实现
 *
 * @param svc 指向要调度的 IPVS 服务的指针
 * @param skb 指向网络数据包的指针
 * @param iph 指向 IP 头部的指针
 * @return 指向选中的目标服务器的指针,如果没有可用的目标服务器,则返回 NULL
 */
static struct ip_vs_dest *
ip_vs_rr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
		  struct ip_vs_iphdr *iph)
{
	struct list_head *p;
	struct ip_vs_dest *dest, *last;
	int pass = 0;

	// 打印调试信息
	IP_VS_DBG(6, "%s(): Scheduling...\n", __func__);

	// 获取调度锁
	spin_lock_bh(&svc->sched_lock);
	// 获取服务的调度数据,即目标服务器列表的头部
	p = (struct list_head *) svc->sched_data;
	// 获取列表中的第一个目标服务器
	last = dest = list_entry(p, struct ip_vs_dest, n_list);

	// 开始调度循环
	do {
		// 遍历目标服务器列表
		list_for_each_entry_continue_rcu(dest,
						 &svc->destinations,
						 n_list) {
			// 如果目标服务器没有过载标志,并且权重大于 0,则选中该服务器
			if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
			    atomic_read(&dest->weight) > 0)
				/* HIT */
				goto out;
			// 如果已经遍历到列表的最后一个服务器,则停止循环
			if (dest == last)
				goto stop;
		}
		// 增加遍历次数
		pass++;
		// 如果前一个目标服务器已经被移除,并且当前遍历次数小于 2,则继续遍历
		/* Previous dest could be unlinked, do not loop forever.
		 * If we stay at head there is no need for 2nd pass.
		 */
	} while (pass < 2 && p != &svc->destinations);

stop:
	// 释放调度锁
	spin_unlock_bh(&svc->sched_lock);
	// 打印错误信息
	ip_vs_scheduler_err(svc, "no destination available");
	// 返回 NULL,表示没有可用的目标服务器
	return NULL;

  out:
	// 更新服务的调度数据,指向下一个要调度的目标服务器
	svc->sched_data = &dest->n_list;
	// 释放调度锁
	spin_unlock_bh(&svc->sched_lock);
	// 打印选中的目标服务器的信息
	IP_VS_DBG_BUF(6, "RR: server %s:%u "
		      "activeconns %d refcnt %d weight %d\n",
		      IP_VS_DBG_ADDR(dest->af, &dest->addr), ntohs(dest->port),
		      atomic_read(&dest->activeconns),
		      refcount_read(&dest->refcnt), atomic_read(&dest->weight));

	// 返回选中的目标服务器的指针
	return dest;
}

/**
 * 定义一个静态的 IPVS 调度器结构体,用于实现轮询调度算法
 */
static struct ip_vs_scheduler ip_vs_rr_scheduler = {
	/**
	 * 调度器的名称,这里设置为 "rr",代表轮询(Round-Robin)调度算法
	 */
	.name =			"rr",			/* name */

	/**
	 * 调度器的引用计数,使用 ATOMIC_INIT(0) 初始化,确保初始值为 0
	 */
	.refcnt =		ATOMIC_INIT(0),

	/**
	 * 调度器所属的模块,THIS_MODULE 是一个宏,代表当前模块
	 */
	.module =		THIS_MODULE,

	/**
	 * 初始化一个链表头,用于管理调度器的相关数据
	 */
	.n_list =		LIST_HEAD_INIT(ip_vs_rr_scheduler.n_list),

	/**
	 * 指向初始化服务的函数指针,这里设置为 ip_vs_rr_init_svc 函数
	 */
	.init_service =		ip_vs_rr_init_svc,

	/**
	 * 指向添加目标服务器的函数指针,这里设置为 NULL,表示不支持添加目标服务器的操作
	 */
	.add_dest =		NULL,

	/**
	 * 指向删除目标服务器的函数指针,这里设置为 ip_vs_rr_del_dest 函数
	 */
	.del_dest =		ip_vs_rr_del_dest,

	/**
	 * 指向调度算法的函数指针,这里设置为 ip_vs_rr_schedule 函数
	 */
	.schedule =		ip_vs_rr_schedule,
};

/**
 * @brief 模块初始化函数,用于注册 IPVS 轮询调度器
 *
 * @return 0 表示成功,负值表示失败
 */
static int __init ip_vs_rr_init(void)
{
	// 调用 register_ip_vs_scheduler 函数注册调度器
	return register_ip_vs_scheduler(&ip_vs_rr_scheduler);
}

/**
 * @brief 模块清理函数,用于注销 IPVS 轮询调度器
 */
static void __exit ip_vs_rr_cleanup(void)
{
	// 调用 unregister_ip_vs_scheduler 函数注销调度器
	unregister_ip_vs_scheduler(&ip_vs_rr_scheduler);

	// 调用 synchronize_rcu 函数等待所有引用计数为 0 的对象被释放
	synchronize_rcu();
}

// 初始化函数,在模块加载时调用
module_init(ip_vs_rr_init);

// 清理函数,在模块卸载时调用
module_exit(ip_vs_rr_cleanup);

// 模块描述
MODULE_DESCRIPTION("ipvs round-robin scheduler");

// 模块许可证
MODULE_LICENSE("GPL");

这段代码实现了轮询调度算法。它遍历目标列表,寻找一个没有过载且权重大于 0 的目标。如果找到了这样的目标,它就会跳出循环。如果没有找到,它会继续遍历,最多遍历两次。

标签:rr,LVS,dest,ip,vs,调度,IPVS,详解,服务器
From: https://www.cnblogs.com/lele0120/p/18593889

相关文章

  • 【软件工程】第三章·计划和管理项目(详解活动图计算关键路径、最早开始时间、最晚开始
    ......
  • 最短路----Dijkstra算法详解
    简介迪杰斯特拉(Dijkstra)算法是一种用于在加权图中找到单个源点到所有其他顶点的最短路径的算法。它是由荷兰计算机科学家艾兹格·迪科斯彻(EdsgerDijkstra)在1956年提出的。Dijkstra算法适用于处理带有非负权重的图。迪杰斯特拉算法主要特点是从起始点开始,采用贪心算法,每次遍历......
  • 十六、操作符详解(1)
    一、算术操作符+  -  *  /  %/,两个操作数至少有一个为浮点数,结果才能为浮点数,保留小数部分结果。%,操作符必须都为整数,且结果也为整数。二、移(二进制补码位)位操作符<<左移                     >>右移对于移位操作符,不能移......
  • 近期做到的有点绕的RSA题目详解(重点:e和phi不互素)
    标题简述:内容是第一遍学,不扎实,遇到一些题目还是无法下手或者有思路有误等等ctfshow1.funnyrsa1e1=14606334023791426p1=1210097727354602353649406229894338076192119260154940874536747476143312950400636797224222982865494936981506906949651061038223153784619701......
  • 深入详解机器学习基础中的模型评估方法
    引言        机器学习正在快速改变我们的世界,从自动驾驶汽车到个性化推荐系统,其应用无处不在。然而,一个成功的机器学习项目不仅依赖于强大的算法和丰富的数据,还需要精确的模型评估方法。模型评估是机器学习过程中不可或缺的环节,它通过衡量模型对新数据的预测能力,来指......
  • HTTP协议详解
    HTTP协议详解https://www.bilibili.com/video/BV1js411g7Fw/P10x00-HTTP协议概念及工作流程P20x01-HTTP协议之方法与状态码options探测支持哪些方法200-服务器成功返回网页301/2-永久/临时重定向304NotModified-未修改失败的状态码:404-请求的网页不存在503-服务器......
  • Linux常用命令之pstree命令详解
    pstree是Linux系统中一个非常有用的命令行工具,它以树状图的形式展示进程间的关系。与传统的ps命令不同,pstree提供了一种更加直观的方式来查看哪些进程是父进程,哪些是子进程,以及它们是如何组织在一起工作的。通过这种方式,用户可以更容易地理解系统中进程的层次结构,并且......
  • lvs搭建负载均衡
    vip:192.168.56.120#设置VIPipaddradd192.168.56.120/24devenp0s8#添加路由表routeadd-host192.168.56.120devenp0s8ipvsadm-C#擦除之前的设置ipvsadm-Lnipvsadm-A-t192.168.56.120:80-srr#添加一个TCP类型的虚拟服务,IP为192.168.56.120,端口为80,使......
  • Vue 3 中的 `update:modelValue` 事件详解
    在Vue3中,update:modelValue​事件通常与v-model​指令一起使用,以实现自定义组件的双向数据绑定。以下是对该事件的详细分析:事件定义首先,我们需要在组件中定义update:modelValue​事件。可以使用defineEmits​函数来声明组件可以发出的事件:constemit=defineEmits([......
  • Vue 3 中的 `update:modelValue` 事件详解
    在Vue3中,update:modelValue​事件通常与v-model​指令一起使用,以实现自定义组件的双向数据绑定。以下是对该事件的详细分析:事件定义首先,我们需要在组件中定义update:modelValue​事件。可以使用defineEmits​函数来声明组件可以发出的事件:constemit=defineEmits([......