Opsqueue:為重負(fù)載而生的輕量級(jí)批處理隊(duì)列,已開源!

大家好,這里是架構(gòu)資源棧!點(diǎn)擊上方關(guān)注,添加“星標(biāo)”,一起學(xué)習(xí)大廠前沿架構(gòu)!

關(guān)注、發(fā)送C1即可獲取JetBrains全家桶激活工具和碼!

image

最近,Channable 團(tuán)隊(duì)發(fā)布了一個(gè)新的開源項(xiàng)目 —— Opsqueue。這是為了解決“重批處理”需求所設(shè)計(jì)的一套輕量級(jí)隊(duì)列系統(tǒng),現(xiàn)在終于公開了!

說(shuō)白了,Opsqueue 是為那些動(dòng)輒幾百萬(wàn)操作、強(qiáng)調(diào)吞吐量、注重處理順序、又不想部署一整套 Kafka 或 Celery 的團(tuán)隊(duì)準(zhǔn)備的。

為什么需要另造一個(gè)輪子?

有幾個(gè)明確的需求:

  • 輕量簡(jiǎn)潔:用 Rust 寫的小巧代碼庫(kù),依賴少,部署容易。
  • 批處理優(yōu)先:強(qiáng)調(diào)吞吐量,而非實(shí)時(shí)性,適合“今天生成,明天處理”的模式。
  • 彈性可擴(kuò)展:支持水平擴(kuò)展,能夠處理十億級(jí)別的操作量。
  • 技術(shù)棧扎實(shí):SQLite + 對(duì)象存儲(chǔ)(S3、GCS 等)+ Rust,穩(wěn)定、可靠、性能高。
  • 部署簡(jiǎn)單:?jiǎn)蝹€(gè)二進(jìn)制,嵌入式數(shù)據(jù)庫(kù),最小配置即可上手。

Channable 團(tuán)隊(duì)之前試過各種方案,從 Redis 列表到 PostgreSQL 表格隊(duì)列,還有數(shù)據(jù)庫(kù)元數(shù)據(jù) + 對(duì)象存儲(chǔ)混合方案……但總歸不是特別理想:不是易錯(cuò)就是擴(kuò)展性差。于是我們決定,“干脆造一個(gè)真正通用的隊(duì)列系統(tǒng)”!

設(shè)計(jì)核心:“生成 - 執(zhí)行” 模式

在很多服務(wù)中都看到類似的流程:

  1. A 服務(wù)生成數(shù)百萬(wàn)個(gè)操作(例如一堆圖像處理任務(wù)、API 調(diào)用、LLM 推理等);
  2. 把這些操作扔進(jìn)“輸入隊(duì)列”;
  3. B 服務(wù)(或多臺(tái)服務(wù))并行執(zhí)行這些任務(wù);
  4. 然后把結(jié)果寫回“輸出隊(duì)列”;
  5. A 服務(wù)再根據(jù)結(jié)果做下一步處理(例如拼裝成結(jié)果、觸發(fā)后續(xù)邏輯等)。

我們把這個(gè)流程稱為 “generate-execute” 模式,它是 Producer-Consumer 的一個(gè)進(jìn)階變種 —— 類似于 MapReduce,但沒有 reduce,也可以看作是并行 Map 操作的分布式實(shí)現(xiàn)。

image

我們希望的是:生產(chǎn)者(Producer)只管提交任務(wù),消費(fèi)者(Consumer)按需并行處理,并且結(jié)果可以有序地回傳給生產(chǎn)者。這個(gè)閉環(huán)非常重要。

為什么不選現(xiàn)有的隊(duì)列系統(tǒng)?

先看簡(jiǎn)單對(duì)比:

特性 Kafka RabbitMQ Redis List Opsqueue
有序性保障 分區(qū)級(jí)順序 僅支持 FIFO 隊(duì)列 無(wú)強(qiáng)一致順序保障 完整任務(wù)有序性
部署復(fù)雜度 中等偏上 極低(單文件即可運(yùn)行)
消費(fèi)端靈活度 高(需自實(shí)現(xiàn)) 高,支持動(dòng)態(tài)調(diào)度策略
數(shù)據(jù)持久性 高(結(jié)合對(duì)象存儲(chǔ))
支持大批量 適合但偏實(shí)時(shí) 支持但繁瑣 不適合 極其適合批處理
使用門檻 較高 中等 極低 低(Rust 單二進(jìn)制部署)
  • Kafka 是高吞吐利器,但維護(hù) ZooKeeper、Broker、Schema Registry 不易;

  • RabbitMQ 雖然靈活但 AMQP 配置繁瑣;

  • Redis List 容易入門但擴(kuò)展性差,且不具備任務(wù)元信息能力。

而 Opsqueue 以 Rust 編寫、只依賴 SQLite 和對(duì)象存儲(chǔ),初始部署成本幾乎為零,卻在批處理吞吐、數(shù)據(jù)一致性、任務(wù)調(diào)度靈活性上做到了高度平衡。
雖然像 RabbitMQ、Kafka、SQS 等已經(jīng)非常成熟,但對(duì)我們來(lái)說(shuō),它們存在如下幾個(gè)痛點(diǎn):

  1. 無(wú)法保證有序性:我們需要任務(wù)提交和返回能對(duì)齊。
  2. 提交要么全成要么全失敗:不能部分成功,這樣處理邏輯才干凈。
  3. 要抗網(wǎng)絡(luò)問題,還要保持一致性:我們偏向一致性而不是可用性。
  4. 隊(duì)列不能餓死某些用戶:要確保使用公平。
  5. 操作必須是任意格式:支持任何語(yǔ)言,任何數(shù)據(jù)類型。
  6. 優(yōu)先級(jí)要由消費(fèi)者決定:因?yàn)閳?zhí)行方才最清楚什么該先做(比如 GPU 使用率、API 限流等)。

市面上的系統(tǒng)幾乎沒有同時(shí)滿足這些點(diǎn)的。

技術(shù)選型:Rust + SQLite + 對(duì)象存儲(chǔ)

Opsqueue的目標(biāo)是 部署簡(jiǎn)單,維護(hù)輕松,開發(fā)友好。所以O(shè)psqueue選擇:

  • Rust 實(shí)現(xiàn),編譯成單個(gè)可執(zhí)行文件;
  • SQLite 存儲(chǔ)操作元數(shù)據(jù)(輕量 + 嵌入式);
  • S3/GCS 等對(duì)象存儲(chǔ)保存真正的任務(wù)數(shù)據(jù);
  • 支持 Litestream 實(shí)時(shí)備份數(shù)據(jù)庫(kù);
  • 所有網(wǎng)絡(luò)交互盡量保持在亞毫秒級(jí)。

這種設(shè)計(jì)讓Opsqueue可以輕松運(yùn)行數(shù)百個(gè)獨(dú)立隊(duì)列,測(cè)試也非常容易 —— 每個(gè)測(cè)試用例起一個(gè) Opsqueue 實(shí)例毫秒級(jí)完成。

技術(shù)架構(gòu)亮點(diǎn)

  • 任務(wù)分片(chunking)設(shè)計(jì):一次性提交幾十萬(wàn)個(gè)操作分成多個(gè) chunk,每個(gè) chunk 幾秒完成,提升并發(fā)度與容錯(cuò)能力;
  • 元數(shù)據(jù)存 SQLite,任務(wù)體存對(duì)象存儲(chǔ)(如 S3/GCS):減輕數(shù)據(jù)庫(kù)壓力,提高 I/O 性能;
  • 消費(fèi)者優(yōu)先調(diào)度機(jī)制:任務(wù)優(yōu)先級(jí)可由消費(fèi)者決策,例如根據(jù) GPU 利用率、用戶 ID、API 調(diào)用限額等;
  • Litestream 支持:通過持續(xù)同步 SQLite 數(shù)據(jù)庫(kù)至遠(yuǎn)端對(duì)象存儲(chǔ)實(shí)現(xiàn)高可用備份;
  • 可視化性能分析:內(nèi)置 OpenTelemetry,便于鏈路追蹤和延遲分析。

消費(fèi)者優(yōu)先策略:我們最大的殺手锏

傳統(tǒng)隊(duì)列系統(tǒng)通常由 生產(chǎn)者來(lái)決定任務(wù)優(yōu)先級(jí),而 Opsqueue 反其道而行之:讓消費(fèi)者決定優(yōu)先順序。

為啥?因?yàn)橄M(fèi)者最清楚實(shí)際資源情況 —— 哪臺(tái)機(jī)器空閑?哪個(gè)用戶快被限流了?哪個(gè)數(shù)據(jù)中心比較空?

Opsqueue支持生產(chǎn)者附帶元數(shù)據(jù)(比如用戶 ID、優(yōu)先級(jí)數(shù)字),但最終決策權(quán)歸消費(fèi)者所有。這種機(jī)制非常靈活,可以支持更復(fù)雜的調(diào)度策略,比如:

  • 某類任務(wù)只能在指定區(qū)域執(zhí)行;
  • 保障用戶公平;
  • 優(yōu)先執(zhí)行短任務(wù)提高吞吐;
  • GPU 資源有限時(shí)自動(dòng)排隊(duì);
  • 跨業(yè)務(wù)統(tǒng)一調(diào)度等。

性能與擴(kuò)展性:小而美,也能頂?shù)米〈箫L(fēng)浪

為了支持十億級(jí)別操作,Opsqueue采取了一些設(shè)計(jì)策略:

  • 任務(wù)按“chunk”提交:每個(gè) chunk 處理時(shí)間控制在幾秒內(nèi),降低協(xié)調(diào)成本;
  • 數(shù)據(jù)存對(duì)象存儲(chǔ),SQLite 僅存元數(shù)據(jù):存儲(chǔ)壓力低,查詢快;
  • chunk 大小可調(diào)節(jié):更好地平衡并發(fā)度與通信開銷;
  • 支持分片(sharding):當(dāng)規(guī)模再上去時(shí),輕松橫向擴(kuò)展;
  • 內(nèi)置 OpenTelemetry tracing:性能指標(biāo)一目了然。

來(lái)看一張調(diào)試圖,展示兩個(gè)消費(fèi)者同時(shí)連接隊(duì)列時(shí)的 tracing 數(shù)據(jù),注意 —— 隊(duì)列通信開銷僅僅是亞毫秒級(jí)!

image

Rust:不是炫技,而是效率保障

Opsqueue不僅用 Rust 實(shí)現(xiàn)主程序,還通過 FFI 接口暴露給 Python 用作客戶端。邏輯全在 Rust 中統(tǒng)一實(shí)現(xiàn),避免多語(yǔ)言重復(fù)造輪子,也大大降低了 bug 風(fēng)險(xiǎn)。

目前Opsqueue已支持 Python 的客戶端開發(fā),入門示例點(diǎn)這里。未來(lái)要加 Java、Go、Node.js?可能一個(gè)下午就能搞定!

image

在生產(chǎn)環(huán)境的表現(xiàn)如何?

Opsqueue 已經(jīng)在Channable 團(tuán)隊(duì)生產(chǎn)環(huán)境里跑了快半年。一條生產(chǎn)隊(duì)列:

  • 完成了 10 萬(wàn)次提交;
  • 處理速率穩(wěn)定在每小時(shí) 100 萬(wàn)操作;
  • 通信延遲始終維持在毫秒級(jí)以內(nèi);
  • 整體表現(xiàn)令人滿意!

當(dāng)然,Channable 團(tuán)隊(duì)不會(huì)強(qiáng)制重寫已有的其他系統(tǒng)(畢竟重寫是個(gè)大坑),但凡是要維護(hù)、擴(kuò)展、或踩了老系統(tǒng)性能瓶頸的項(xiàng)目,Opsqueue 是可以考慮的替代方案!

寫在最后

我相信:你可能也遇到過類似的需求。希望 Opsqueue 這個(gè)項(xiàng)目能幫上忙,或者激發(fā)你構(gòu)建更好的隊(duì)列系統(tǒng)!

GitHub 倉(cāng)庫(kù)

祝你的任務(wù)隊(duì)列永不卡頓,批處理永不爆炸!

本文由博客一文多發(fā)平臺(tái) OpenWrite 發(fā)布!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容