无意中在应用层瞥见了一个微内核的操作系统调度器

news/2025/9/22 21:08:32/文章来源:https://www.cnblogs.com/thisiswhy/p/19106038

你好呀,我是歪歪。

最近遇到一个业务上的问题,在网上看到一个对应场景下的解决方案,我感觉这个场景还挺有通用性的,分享一下。

以后遇到类似问题,或者当它以面试场景题出现的时候,你可以拿去就用。

事情是这样的。

程序里面有一条“线路”,这个“线路”是购买的外部服务,使用起来是要收费的。

为了更好的理解这个“收费的线路”,你可以假设为这是一个付费的 AI 接口。

然后你可以把“线路”简单的理解为一个 FIFO 的公共队列,对应着多个生产者。

也就是同时有很多人,即消费者,在使用这个“AI 接口”提问。

画个示意图是这样的:

理想情况下,我们期望大家和和气气轮流用,你一个我一个,节奏均匀得像心跳。

但是,实际使用过程中,可能会出现一个卷王 A 生产者,突然快速的生产了大批量的数据,导致 B、C 生产者产生的少量的数据排在队列的最后面,等到天荒地老:

整个队列呈现出的短时间内只为 A 生产者服务的效果。

即 A 生产者“长时间霸占”了整个队列。

很明显,这样对其他生产者不友好。

我在网上查询了一下,这个现象还有一个专门的名词,叫做吵闹邻居问题(Noisy Neighbor Problem)。

主要是指在多租户环境中,单个用户过度占用资源导致其他用户服务质量下降的现象。

常见的方案

针对这个问题,常见的方案一般有两个。

第一个是把队列,即“线路”分来,就像这样:

各玩儿各的,互不干扰。

没有邻居,也就不存在“吵闹邻居”的问题。

这样可以解决问题,但是会带来一个新的问题。

前面说了,这个“线路”是有购买成本的。

如果为每一个消费者都提供一个单独的队列,即上面说的“线路”,那成本就太高了。

那你可能会反驳一句:不需要为每个人提供单独的队列,只为高频使用的人员提供就行了嘛。

是的,这样也没有毛病。

但是实际情况是,高频使用的人, 也只是在某个小段时间内高频使用,随后就是长期的闲置,浪费购买成本。

而且,在实际情况中,还会出现一个情况是,某个低频使用的用户,突然在某一段时间出现业务高峰。

那这种情况为了不影响其他用户,还得紧急给业务高峰的用户搞个专门的队列。

运营成本太高。

所以,这个方案适用于长期稳定都是高频用户的情况。

第二个方案是限制生产者的生产速度。

这个方案在解决问题的同时也带来了新问题。

第一个问题是我需要实现一个限流功能,提升了基础组件的复杂度。

第二个问题是由于下游有限流机制,那上游必然就要有重试机制,增加了整体系统的复杂度。

这两个常见的方案,一个烧钱,一个烧脑。

我了解了之后,发现和我的场景都不太匹配,不能直接使用。

Amazon SQS

在我向大模型求助的时候,它给了我这样一个关键词:

智能调度算法:正如Amazon SQS所使用的公平队列(Fair Queueing) 机制,在软件层面确保资源被公平地分配给所有用户,防止任何一个用户垄断资源。

于是我在网上找到了 Amazon 官方网站中这个文章:

https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-fair-queues.html

从文章中的描述看,它有一个识别谁是“吵闹邻居”的机制:

当识别到 A 是一个“吵闹邻居”之后,Amazon SQS 会把其他租户(B、C 和 D)的消息放在最前面。

这里的“租户”,你可以认为就是我们前面提到的生产者。

这种优先级有助于保持安静租户 B、C 和 D 的低停留时间,而租户 A 的消息停留时间会延长,直到队列积压被消耗,而不会影响其他租户:

看起来确实能解决我的问题。

于是追问了一下大模型关于它的问题,想要进一步了解一下底层原理:

从大模型的回答来看,它核心逻辑是有一个“动态权重调整机制”。

“动态权重调整机制”的目的,我个人理解是为了给每个生产者一个合适的权重,从而决定这次生产的任务是应该放在队列的前面还是后面。

大概是这个意思:

初步了解之后,感觉它底层实现还有点复杂,我把握不住。

有一种杀鸡用牛刀的感觉,所以我不打算使用它。

但是也不算白忙活,至少知道了 Amazon SQS 这个东西的存在。

换个思路

于是我在网上继续搜索,找到了这篇文章,它描述的思路,完美解决了我的问题:

https://densumesh.dev/blog/fair-queue/

而且它的思路很简单,简单到让我觉得如果让我深入的思考一下,也许我也能想到这个方案。

它的核心思路,用文章中的这张图就能说清楚:

给每个生产者分配一个存放 messages 的队列,同时给每个生产者分配一个 client id。

然后你注意看这里:

把 client id 放在一个 Round Robin Queue。

这是个什么玩意?

其实就是一个简单的轮询策略。

先从队首取出 client id。

然后由选择出的 client id 找到对应的 work 去从对应的队列中取出消息来消费。

最后视情况而定,是否需要把这个 client id 放在队尾。

由于轮询机制,所以会确保各个生产者的消息是交替执行。

作者使用 Rust 语言实现了上面的逻辑,并取名叫做:Broccoli。

翻译过来是一个我不喜欢吃的蔬菜:西兰花。

这是对应的仓库链接:

https://github.com/densumesh/broccoli

这个“西兰花”的核心架构非常简单,主要有两个主要组件:每个客户端的专用队列和单个轮转调度器。

对于基础组件来说,设计越简单,就越可靠。

作者在文章中也介绍了核心逻辑的伪代码:

并不复杂,只有几行代码,我带你盘一盘。

核心逻辑分为两坨。

第一坨逻辑是产生新消息,对应插入操作。

首先,将某个生产者新产生的消息存储在一个专门的对应生产者的队列中。

然后,检查这个生产者对应的 client id 是否已经在轮询队列中。

如果在,那就完事了。

如果不在,那就把这个 client id 加在轮询队列的末尾。

只要放到轮询队列里面去了,就只需要等着被调度就行了。

第二坨逻辑是消费消息。

首先,从轮询队列中获取队首的 client id。

然后,从这个 client id 的专属队列中获取一条消息进行处理。

处理完毕后,检查这个 client id 的专属队列是否还有消息。

如果专属队列空了,这个 client id 就不需要放回到轮询队列了。

如果专属队列还有消息,那把这个 client id 放回到轮询队列的队尾,就完事了。

看起来逻辑确实非常简单、清晰。

这个方法的优点在于它完全能自我平衡。

“吵闹的邻居”会留在轮询队列中,“空闲的邻居”会自动退出,并且无论他们排队的工作量有多少,每个人都能公平地获得处理时间。

看到这里有的小伙伴可能会产生一个疑问:前面不是说了如果为每一个消费者都提供一个单独的队列,即付费“线路”,成本太高了吗?那为什么这里就可以一人一个呢?

我把上面的图,再多画一点出来,你就明白了:

这里的“一人一个”是真的就一个队列而已。

经过这套逻辑之后,各个生产者的消息会呈现出交叉串行的形态,再穿过真正的“付费线路”。

“付费线路”只有一条,并没有产生额外的费用。

代码

思路有了,代码不是手到擒来的事儿吗?

我这里也不给你粘代码了,直接给你上个代码截图:

但是我可以告诉你怎么去获取对应的代码实现。

去问大模型就行了。

我把流程示意图和伪代码描述直接扔给 DeepSeek:

让它给我一份 Java 代码,就行了:

如果你让我按照上面的思路去敲代码,我觉得我至少得写 30 分钟才能把初版写好,而且这都算是快的。

现在,大模型会在一分钟内给你安排的明明白白:

你只需要最终检查一下代码逻辑就行了。

哎,我已经忘记上次纯古法手工敲代码是什么时候的事儿了。

眼熟吗?

另外,你有没有发现这个“Broccoli”方案,有点眼熟?

这不就是操作系统的进程调度器玩了几十年的经典套路吗?

早期的操作系统或某些简单场景下,CPU 调度使用先来先服务(FCFS)策略。

先到的进程先获得 CPU,执行完了才轮到下一个。

如果一个“计算密集型”的进程(比如 A 用户)拿到 CPU,它可能执行很长时间(比如一个耗时循环),导致后面所有“交互密集型”的进程(比如 B、C 用户的轻量任务)都被阻塞,系统响应速度急剧下降。

这不就是“吵闹邻居”问题的翻版吗?

A 进程就是那个吵闹邻居。

为了解决 FCFS 的公平性问题,操作系统引入了时间片轮转调度算法。

这几乎和“Broccoli”的方案一模一样。

核心思想是为每个进程分配一个固定的时间片。

进程在 CPU 上运行一个时间片后,就会被强制剥夺 CPU 使用权,并排到就绪队列的末尾,让下一个进程运行。

对应“西兰花”来说:

  • CPU 时间片 ->你的调度器每次从用户专属队列里取出一个任务进行处理。
  • 就绪队列 -> 你的 Round Robin Queue(轮询队列),里面放的是有任务待处理的用户 ID。
  • 剥夺 CPU 并排到队尾 -> 处理完一个用户的一个任务后,如果他还有任务,就把他的用户 ID 重新放回轮询队列的队尾。

现在看这张图,是不是感觉它就是一个微型的操作系统调度器?

而且,操作系统还有更高级的玩法——多级反馈队列。

这可以给“西兰花”未来的优化提供方向:

  • 多队列:设置多个不同优先级的队列。新来的进程先进入最高优先级队列。
  • 时间片不同:高优先级队列的时间片短(保证响应快),低优先级队列的时间片长(提高吞吐量)。
  • 反馈机制:如果一个进程在一个时间片内用完了还没结束,说明它可能是“长任务”,就把它降级到低优先级队列。如果一个进程在时间片用完前主动放弃 CPU(比如进行I/O操作),说明它可能是交互式的“短任务”,就让它留在高优先级队列。

另外,操作系统调度磁盘 I/O 请求时,也会遇到一模一样的问题。

如果完全按照 FIFO,某个进程的大量顺序读写请求会霸占磁头,导致其他进程的随机读写请求饥饿。

解决方案之一就是电梯算法(SCAN) 或其变体,其核心也是将单个 FIFO 队列拆解,重新排序请求,以在公平性和效率之间取得平衡。

这和我们前面的思路,可以说是同宗同源。

所以,恭喜你,无意中在应用层瞥见了一个微内核的操作系统调度器!

最后,在上个价值。

计算机科学中很多看似复杂的底层原理,其核心思想都具有极强的普适性。

真正优秀的设计模式,会反复出现在从硬件到应用、从底层到高层的各个层面。

下次再有人问你如何解决资源争抢问题,你不仅可以甩出“西兰花”方案,还可以淡定地补充一句:

这其实就是操作系统级的时间片轮转调度算法在分布式系统中的应用。我们不过是在业务层,用最低的成本,复刻了操作系统几十年来验证过的公平性智慧。

好了,就到这里了。

这个方案之前是别人的,后来变成了我的,现在是你的了。

不客气,来个三连就行。

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

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

相关文章

数据结构思维题选做(长期更新)

到处乱找的. 用到的数据结构在 NOIP 考纲内,主要是学习、锻炼各种处理思路. 代码的实现都不算困难. 倍增思想 P10198 [USACO24FEB] Infinite Adventure P Hint:跳的步数明显提示倍增. 根据 \(\sum T_i\) 的限制直接预…

龙泉公路建设投资有限公司网站网站到期怎么续费

Prometheus是一款基于时序数据库的开源监控告警系统,非常适合Kubernetes集群的监控。Prometheus的基本原理是通过HTTP协议周期性抓取被监控组件的状态,任意组件只要提供对应的HTTP接口就可以接入监控。不需要任何SDK或者其他的集成过程。这样做非常适合做…

网站建设宣传视频教程河北移动端网站制作

1.安装docker服务,配置镜像加速器 2.下载系统镜像(Ubuntu、 centos) 3.基于下载的镜像创建两个容器 (容器名一个为自己名字全拼,一个为首名字字母) 4.容器的启动、 停止及重启操作 5.怎么查看正在运行的容器…

陕西省建设监理协会网站证件查询什么是网络营销产生的技术原因

Swift 可选(Optionals)类型 Swift 的可选(Optional)类型,用于处理值缺失的情况。可选表示"那儿有一个值,并且它等于 x "或者"那儿没有值"。 Swfit语言定义后缀?作为命名类型Optional的简写&…

cdn网站加速如何免费创建自己的小程序

说明 在大量数据处理任务下的缓存与分发 这个算是来自顾同学的助攻1,我有点java绝缘体的体质,碰到和java相关的安装部署总会碰到点奇怪的问题,不过现在已经搞定了。测试也接近了kafka官方标称的性能。考虑到网络、消息的大小等因素&#xff0…

网站建设初稿哪家建站好

初始标记 需要暂停所有的其他线程,但这个阶段会很快完成。它的目的是标记所有的根对象,以及被根对象直接引用的对象,以及年轻代指向老年代的对象,不会遍历对象关系,单线程执行。 并发标记阶段 不需要暂停应用线程&a…

营销型网站建设和规划lnmp下安装wordpress

文章目录 四、流程控制语句4.1 选择结构4.1.1 if语句 4.1.2 三目运算符4.1.3 switch语句注意事项 4.1.4 if和switch的区别【CHAT】4.2 循环结构4.2.1 while循环语句4.2.2 do...while循环语句 4.2.3 for循环语句九九乘法表 4.3 跳转语句4.3.1 break语句4.3.2 continue语句4.3.3 …

政治笔记/错题

生产力:是指人们改造自然,使之适应人的需要的物质力量,标志着人类改造自然的实际能力和水平。 生产关系:生产力诸要素相结合的社会形式,指的是人们在物质生产和再生产过程中所形成的经济关系,它是由生产资料所有…

9.22模拟赛总结

赛时 模拟赛 赛时记录: 剩余时间2:33开始打T2 剩余时间1:32开始打完T2 剩余时间1:00结束T2 剩余时间0:30T4有思路,开始打 时间到了放弃T4 赛时状态: T1怎么看起来好复杂,完全没有顺序和思路 T2回文串,每个位置…

莫队 n的序列,多次查询一段区间内的数字的个数

莫队 n的序列,多次查询一段区间内的数字的个数 // 普通莫队 O(n*sqrt(n)) include include include include using namespace std; const int N=50005; int n,m,k,B,a[N]; int sum,c[N],ans[N]; struct Q{ int l,…

巴州建设局网站网站建设与app开发

任务一:挖掘反射型XSS漏洞(以弹窗test13)证明 任务二:复现环境中的CSRF漏洞,设计表单,当管理员点击URL后自动将自己密码重置为:123456 任务三:复现环境中的JSON Hijacking漏洞&#…

免费发布推广的网站wordpress页面几层

https://support.huawei.com/enterprise/zh/doc/EDOC1100325140/f6eeacd6 打开链接,里面的内容很详细。

建设银行官方网站个人深圳市建设交易中心官网

应用场景 有的时候,我们对于同一通道中的消息处理,会通过判断头信息或者消息内容来做一些差异化处理,比如:可能在消息头信息中带入消息版本号,然后通过if判断来执行不同的处理逻辑,其代码结构可能是这样的…

杭州网站运营国外排版网站

目录 概述 环境依赖 数据描述 代码概述 导包 数据读取 统计缺失值 数据结构概述 描述统计 时间轴数据转换 月交易统计直方图 周交易统计图 小时数据转换 小时折线图 销售关系可视化统计 销售占比扇形图 价格箱线图 各类别多维度条形图统计 商店位置交易量折线…

查企企官方网站品牌推广公司排行榜

作者简介 多肉,饿了么资深python工程师。曾在17年担任饿了么即时配送众包系统的研发经理,这篇文章最早的版本就诞生于那段时间,目前负责配送相关业务系统的整体稳定性建设。个人比较喜欢c和python,最近有点迷rust,同时…

专业集团网站建设小程序app怎么做

目录 uni-app介绍 uni-app开发工具HBuilderX 创建项目前提条件 uni-app项目结构 配置mumu模拟器 uni-app生命周期 1.应用生命周期 小程序规范 2.页面生命周期-小程序规范 3.组件生命周期 vue规范 uni-app登录按钮方法 uni-app发布安卓app uni-app介绍 uni-app 是一个…

南通网站建设果尔浙江网站建设dyfwzx

文章目录 前言声明一、漏洞描述二、漏洞分析三、漏洞复现四、修复建议前言 泛微e-office OfficeServer2.php 存在任意文件读取漏洞,攻击者可通过构造特定Payload获取敏感数据信息。 声明 请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造…

商业网站的建设与维护wordpress用redis

排序 排序的概念常见的排序算法常见排序算法的实现数组的打印 插入排序直接插入排序的实现希尔排序( 缩小增量排序 )希尔排序的实现 交换排序冒泡排序冒泡排序的实现选择排序选择排序的实现堆排序堆排序的实现快速排序快速排序非递归 归并排序归并排序的递归实现归并排序的非递…

【mysql】mysql客户端中文显示乱码

背景 最近在做数据库还原的时候,由于备份的sql中存在中文,导致还原的时候,出现乱码;深受毒害 解决 修改 MySQL 配置文件 my.cnf或 my.ini​ 在 ​MySQL 配置文件​(通常位于 /etc/my.cnf、/etc/mysql/my.cnf或 ~/…

很烦不知道 自己以后要做什么,工作不会很稳定。感觉有很多东西要学习 但是 也有很多东西 不会 不知道咋办了

很烦不知道 自己以后要做什么,工作不会很稳定。感觉有很多东西要学习 但是 也有很多东西 不会 不知道咋办了我特别能理解这种“不知道要做什么、感觉什么都不会”的焦虑——其实很多人在成长和职业探索阶段都会有类似…