调度子系统4_负载均衡(一)

本文涉及的产品
传统型负载均衡 CLB,每月750个小时 15LCU
网络型负载均衡 NLB,每月750个小时 15LCU
EMR Serverless StarRocks,5000CU*H 48000GB*H
简介:
//	参考:http://blog.csdn.net/dog250/article/details/5303561

//	负载均衡
//		当rq->next_balance到时,触发负载均衡	
//	调用路径:scheduler_tick->trigger_load_balance
//	注:
//		nohz.cpu_mask中的cpu表示停用了周期时钟
//	函数任务:
//		1.如果进入tick的时候rq变得有事可做,并且之前由本cpu执行idle load balance
//			1.1 不再做idle load balance
//			1.2 nohz.cpu_mask中选择一个cpu负责idle load balance
//			1.3 通过ipi通知彼cpu负责ilb
//		2.如果所有cpu均处于idle状态,之前由本cpu做idle load balance
//			2.1 没有必要再做idb,通知本cpu停止idle load balance
//		3.如果本cpu处于idle状态,其他cpu做idle load balance
//			3.1 ilb的cpu会代此cpu执行load balance,不需要raise SCHED_SOFTIRQ
//		4.本cpu没有加入到任何domain,则不需要在domain间load balance
//		5.如果到达执行load balance时间点
//			5.1 raise SCHED_SOFTIRQ
1.1 static inline void trigger_load_balance(struct rq *rq, int cpu)
{
#ifdef CONFIG_NO_HZ

	//	rq->idle_at_tick = 0表示rq上运行的非idle进程
	//	rq->in_nohz_recently表示最近关闭了周期时钟
	if (rq->in_nohz_recently && !rq->idle_at_tick) {
		rq->in_nohz_recently = 0;
		//如果之前由本cpu执行idle load balance
		if (atomic_read(&nohz.load_balancer) == cpu) {
			//nohz.cpu_mask中的cpu表示停用了周期时钟,在select_nohz_load_balancer中被加入
			cpumask_clear_cpu(cpu, nohz.cpu_mask);
			//进入tick的时候rq变得有事可做,则不再做idle load balance
			atomic_set(&nohz.load_balancer, -1);
		}
		//从nohz.cpu_mask中选择一个cpu做idle load balance 
		if (atomic_read(&nohz.load_balancer) == -1) {
			int ilb = find_new_ilb(cpu);
			if (ilb < nr_cpu_ids)
			{
				//向该cpu发送ipi
				resched_cpu(ilb);
			}
		}
	}

	//如果所有cpu均处于idle状态,本cpu做idle load balance,则通知本cpu停止idle load balance
	if (rq->idle_at_tick && atomic_read(&nohz.load_balancer) == cpu &&
	    cpumask_weight(nohz.cpu_mask) == num_online_cpus()) {
		resched_cpu(cpu);
		return;
	}

	//如果本cpu处于idle状态,其他cpu做idle load balance,则不需要raise其SCHED_SOFTIRQ
	if (rq->idle_at_tick && atomic_read(&nohz.load_balancer) != cpu &&
	    cpumask_test_cpu(cpu, nohz.cpu_mask))
		return;
#endif
	//本cpu没有加入到任何domain,则不需要raise其SCHED_SOFTIRQ
	if (time_after_eq(jiffies, rq->next_balance) &&
	    likely(!on_null_domain(cpu)))
	{
		//raise其SCHED_SOFTIRQ
		raise_softirq(SCHED_SOFTIRQ);
	}
}


//	负载均衡软中断
//		由trigger_load_balance函数触发
//	函数任务:
//		1.自下而上遍历cpu所属的sched domain,对其进行负载均衡
//		2.如果本cpu负责idle load balance,代停用周期时钟的cpu执行load balance
//			2.1 遍历nohz.cpu_mask中所有的idle cpu
//				2.2.1 代其执行步骤1
//				2.2.2 如果这这段时间内本cpu有非idle进程就绪,退出ilb,下一次负载均衡时发生时再ilb
//				2.2.3 如果idle cpu下一次进行负载均衡的时间戳大于本cpu
//					2.2.3.1 更新idle cpu下一次负载均衡的时间为本cpu进行负载均衡的时间戳
2.1 static void run_rebalance_domains(struct softirq_action *h)
{
	int this_cpu = smp_processor_id();
	struct rq *this_rq = cpu_rq(this_cpu);
	//cpu当前状态
	//	如果rq上当前运行的为idle task则cpu为idle状态
	enum cpu_idle_type idle = this_rq->idle_at_tick ?
						CPU_IDLE : CPU_NOT_IDLE;
	//为cpu在同一个domain内执行load balance
	rebalance_domains(this_cpu, idle);

#ifdef CONFIG_NO_HZ

	//如果本cpu负责idle load balance,代停用周期时钟的cpu执行load balance
	if (this_rq->idle_at_tick &&
	    atomic_read(&nohz.load_balancer) == this_cpu) {
		struct rq *rq;
		int balance_cpu;

		for_each_cpu(balance_cpu, nohz.cpu_mask) {
			if (balance_cpu == this_cpu)
				continue;
			//非idle进程就绪,不在执行idle load balance,下一次load balance发生时再ilb
			if (need_resched())
				break;
			//代idle的cpu执行load balance
			rebalance_domains(balance_cpu, CPU_IDLE);
			//更新本rq下一次load balance的时间为所有被代理rq执行load balance中最早的
			rq = cpu_rq(balance_cpu);
			if (time_after(this_rq->next_balance, rq->next_balance))
				this_rq->next_balance = rq->next_balance;
		}
	}
#endif
}

//	负载均衡
//		在cpu所属的sched domain层次结构上执行load balance
//	调用路径:run_rebalance_domains->rebalance_domains
//	函数参数:
//		idle,cpu处于的状态
//			CPU_IDLE,cpu上运行的idle task
//			CPU_NOT_IDLE,cpu上运行的非idle task

//	函数任务:
//		1.自上而下遍历rq所属的所有sched domain
//			1.1 如果此domain不需要执行load balance(没有设置SD_LOAD_BALANCE),则跳过
//			1.2 计算domain执行load balance的时间间隔
//				1.2.1 由domain的balance_interval指定load balance在domain上执行的时间间隔
//				1.2.2 降低非idle状态的cpu通过放大时间间隔降低load balance执行的频率
//				1.2.3 在HZ*NR_CPUS/10时间内,必须对domain执行一次load balance
//			1.3 如果domain的load balance需要串行执行,则获取balance锁
//			1.4 如果当前时间到达domain load balance执行的时间点
//				1.4.1 从同一个domain的其他cpu拉进程到本cpu执行
//				1.4.2 如果成功从其他cpu上拉进程到本cpu,则设置cpu不再为idle状态
//			1.5 如果domain的load balance需要串行执行,则释放balance锁
//			1.6 通过next_balance记录rq下一次执行load balance的时间为其所属domain中
//				最近的load balance时间
//			1.7 如果返回值balance=0,说明已经完成了负载均衡,退出遍历,否则继续1.1
//		2.更新rq下一次执行load balance的时间为其所属domain中最近的load balance时间
3.1 static void rebalance_domains(int cpu, enum cpu_idle_type idle)
{
	int balance = 1;
	struct rq *rq = cpu_rq(cpu);
	unsigned long interval;
	struct sched_domain *sd;
	//rebalance执行最近的时间
	unsigned long next_balance = jiffies + 60*HZ;
	int update_next_balance = 0;
	int need_serialize;
	//遍历cpu所属的所有sched domain
	for_each_domain(cpu, sd) {
		//此domain不需要load balance
		if (!(sd->flags & SD_LOAD_BALANCE))
			continue;
		//domain执行load balance的时间间隔
		interval = sd->balance_interval;
		//降低非idle状态cpu的load balance的执行频率
		//	通过放大load balance执行的最小间隔达到降低频率的目的
		if (idle != CPU_IDLE)
			interval *= sd->busy_factor;
		interval = msecs_to_jiffies(interval);
		if (unlikely(!interval))
			interval = 1;
		//最大间隔为HZ*NR_CPUS/10
		if (interval > HZ*NR_CPUS/10)
			interval = HZ*NR_CPUS/10;
		//此domain的load balance需要串行执行
		need_serialize = sd->flags & SD_SERIALIZE;
		if (need_serialize) {
			//试图获取串行load balance失败,则放弃此次load balance
			if (!spin_trylock(&balancing))
				goto out;
		}
		//如果当前时间到达domain load balance执行的时间点
		if (time_after_eq(jiffies, sd->last_balance + interval)) {
			//从同一个domain的其他cpu拉进程到本cpu执行
			if (load_balance(cpu, rq, sd, idle, &balance)) {
				//由于已经从其他cpu上拉来了进程,因此本cpu不在是idle状态
				idle = CPU_NOT_IDLE;
			}
			//更新domain上一次load balance的时间为当前jiffies
			sd->last_balance = jiffies;
		}
		if (need_serialize)
			spin_unlock(&balancing);
out:
		if (time_after(next_balance, sd->last_balance + interval)) {
			//next_balance记录下一次执行load balance最近的时间
			next_balance = sd->last_balance + interval;
			update_next_balance = 1;
		}

		//完成均衡
		if (!balance)
			break;
	}
	//更新rq下一次load balance执行时间为所属domain中下一次load balance最早的时间
	if (likely(update_next_balance))
		rq->next_balance = next_balance;
}

相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
目录
相关文章
|
4月前
|
缓存 负载均衡 算法
(四)网络编程之请求分发篇:负载均衡静态调度算法、平滑轮询加权、一致性哈希、最小活跃数算法实践!
先如今所有的技术栈中,只要一谈关于高可用、高并发处理相关的实现,必然会牵扯到集群这个话题,也就是部署多台服务器共同对外提供服务,从而做到提升系统吞吐量,优化系统的整体性能以及稳定性等目的。
|
6月前
|
弹性计算 负载均衡 算法
负载均衡调度算法
负载均衡调度算法介绍
108 2
|
6月前
|
弹性计算 负载均衡 算法
负载均衡调度算法介绍
介绍负载均衡调度算法
201 8
|
负载均衡 算法 调度
负载均衡集群LVS调度算法实战(三)|学习笔记
快速学习负载均衡集群LVS调度算法实战
113 0
负载均衡集群LVS调度算法实战(三)|学习笔记
|
负载均衡 网络协议 算法
负载均衡集群 LVS 调度算法实战(一)|学习笔记
快速学习负载均衡集群 LVS 调度算法实战
136 0
负载均衡集群 LVS 调度算法实战(一)|学习笔记
|
存储 缓存 负载均衡
负载均衡集群LVS调度算法实战
一、复习LVS的四种模式 二、LVS介绍 三、调度算法 四、实操演示
负载均衡集群LVS调度算法实战
|
Web App开发 负载均衡 应用服务中间件
基于apache实现负载均衡调度请求至后端tomcat服务器集群的实现
基于apache实现负载均衡调度请求至后端tomcat服务器集群的实现
2101 0