OPPO自研雲原生分散式任務排程平臺

語言: CN / TW / HK

1.概述

在軟體開發過程中,經常會遇到需要執行定時任務的場景。目前業界執行定時任務的分散式任務排程平臺主要有XXL-Job和Elastic-Job,兩者都屬於輕量級的排程平臺,能滿足一定任務數量的作業同時排程,但是如果任務排程量增加到1萬TPS甚至10萬TPS,就會遭遇效能瓶頸,出現很多超時任務。

在OPPO內部,有些業務部門存在海量作業同時排程的場景,目前業界的任務排程框架難以滿足業務需求,所以OPPO公司的中介軟體團隊自主研發了一個分散式任務排程平臺CloudJob,它是一個高效能(百萬級TPS),低延遲(毫秒級),統一,穩定,精準並滿足複雜多樣定時任務場景的排程平臺。它的特點如下:

(1)簡單:使用者可以通過頁面對任務進行CRUD操作,也可以通過提供的SDK對任務進行管理,方便快捷。

(2)動態:支援動態修改任務狀態、啟動/停止任務,以及終止執行中任務,即時生效。

(3)一致性:“排程中心”通過分散式鎖保證叢集分散式排程的一致性, 一次任務排程只會觸發一次執行。

(4)高效能、低延遲:支援百萬級任務同時排程執行,且延遲在毫秒級。

1.1 和開源產品對比

CloudJob設計的初衷是為了支援海量任務同時排程,它和其它任務排程平臺的對比如下:

對比內容 Elastic-Job XXL-Job CloudJob
執行定時任務的方式 通過quartz觸發任務執行,任務量大時會有延時 採用輪詢jobtrigger的方式排程任務,任務量大時會有延時 輪詢資料庫的觸發訊息,作業進行了分片,任務量 大延時很低
多節點部署時任務不能重複執行 支援 支援 支援
彈性擴容縮容 通過zk實現各服務的註冊、控制及協調,當任務很多時zk會成為效能瓶頸 使用quartz基於資料庫的分散式能力,伺服器超出一定數量會給資料庫造成壓力 通過分片可以支援系統的橫向擴充套件,擴容縮容方便快捷
日誌可追溯 支援,可通過事件訂閱的方式處理排程過程的重要事件,記錄資訊在資料庫中可查詢 支援,有日誌查詢介面 支援,通過鏈路追蹤的方式記錄執行過程,可以同介面查詢歷史記錄
高可用 去中心化的排程方式,通過zookeeper來進行選舉、排程。 如果某個例項失敗,會選舉其它例項來執行 “排程中心”通過DB鎖 保證叢集分散式排程的一致性,一次任務排程只執行一次 叢集內部的定時任務通過Elastic-Job來執行,保證內部任務的高可用; 通過redis快取記錄排程資訊,保證作業不會重複執行

1.2 CloudJob的效能

在公司內部對CloudJob進行了多輪效能測試,通過對測試資料進行分析,CloudJob的效能如下:

(1)低時延:CloudJob在處理TPS為50W的作業排程時,99.11%的作業排程延時在1秒以內;CloudJob處理上億次排程,最大排程延時不超過2.4秒。

(2)高效能:對比Elastic-Job的單個執行器執行上千個任務就會出現大量延時,CloudJob的單個執行器處理上萬個任務仍然可以保證毫米級排程任務而不超時。

(3)擴充套件能力強:效能測試場景TPS由10萬增加到50萬,系統只需要按比例增加執行器個數,依然可以保證作業正常排程而不出現嚴重超時。

(4)高可用:測試過程中將某個執行器宕機,該執行器的作業可以轉移到其它裝置排程,並且排程延時最大不超過3秒。

 2.系統架構

CloudJob的總統架構如下:

2.1 名稱解釋

作業元資料:指作業執行的時間規則以及業務執行需要透傳的引數,存在於mongodb資料庫及快取中。

作業觸發訊息:指作業按照時間規則計算出的產生觸發執行動作的每一條記錄,包含作業主鍵和執行絕對時間時間戳,比如每5s執行一次任務,第5s和第10s是兩個觸發器。

執行器:用於掃描符合條件的作業觸發器的服務所在的容器。

分片:為了讓系統可以橫向擴充套件,需要將作業劃分到不同的分組,每個分組就是一個分片,同時每個執行器設定一個分片屬性(和作業的分片屬性相對應),執行器只處理自己所在分片對應的作業。

定時任務執行週期:執行在執行器的定時任務每隔多久執行一次。本方案中該值的設定主要和觸發器儲存選型有關,應該設定合適的頻率,避免過於頻繁導致寫入和儲存觸發器時延時較大,同時也不能因為過大,導致一些本應該執行的任務不能被及時獲取,出現觸發延遲太多。

時間視窗:定時任務掃描觸發器的時間條件,選出將來一定時間範圍內的資料。注意這個視窗最好不一定等於定時任務的執行週期。

2.2 服務模組組成

作業管理服務:負責作業增刪改查,使用者可以通過作業管理服務將作業註冊到平臺,平臺將作業持久化到mongodb資料庫並在redis中快取。

執行器負載監控服務:在CloudJob平臺中每個執行器會處理一個數據分片,執行器負載監控服務會將作業劃分到不同分片,分片內作業數量將維持在合理的數量範圍,保證作業按照時間規則傳送而不延遲。該服務負責分片的作業容量管理以及分片擴縮容等功能。

觸發器儲存:支援可插拔的儲存,提供高可用方案,保證資料零丟失。

觸發器定時任務:執行器定時執行的操作,主要是掃描mongodb資料庫,生成作業觸發訊息。

執行記錄儲存:記錄傳送到業務MQ 的訊息,核對是否有漏傳送、傳送是否有延遲等,發現系統可能存在的問題並及時對整個系統完善優化。

執行記錄視覺化:通過頁面檢視、查詢作業的歷史記錄,檢視作業是否超時,是否由漏執行。

通過對上面幾個服務模組的說明,可以看出作業在系統中的流轉過程如下:

使用者先通過作業管理服務將作業註冊到mongodb資料庫中,並通過redis來快取作業。執行器負載監控服務對新新增的作業設定分片,獲取當前系統未飽和的最小分片,將這個分片的id設定為這個作業的分片,同時將分片對應的作業數量加1。多個執行器會根據設定好的分片引數定時從mongodb資料庫中掃描出符合條件的作業,然後根據作業的時間表達式生成作業觸發訊息,然後將觸發訊息寫入到時間輪中,最後在作業達到執行時間時將作業的基本資訊投送到訊息佇列中,讓使用者從訊息佇列取出訊息並執行自己的業務邏輯,從而達到觸發作業排程的目的。

3. 資料流轉舉例

定時任務按照執行次數可以分為固定週期型別和固定延遲型別。固定週期型別是指作業按照一定的週期每隔一段時間執行,固定延遲型別是指會在延遲一段時間後,執行一次,隨後就不會再執行。下面舉例說明這兩種型別的任務是如何流轉的。

3.1 固定週期型別

使用者建立了一個固定週期型別的任務,每隔5s執行一次,攜帶的引數為

CRON 0/5 * * * ?*

其它透傳引數

作業管理服務先投遞到MQ 普通訊息。消費者持久化該條資料,獲得主鍵jobpk1,計算出來該定時任務後續的觸發時間戳為1612493460,儲存到觸發器的儲存中,必要欄位為:

JobId

1612493460

執行器上的定時任務在做掃描時,掃描到了該條觸發器資料,判斷是否到了預期投遞時間,如果已經到了直接投遞到業務MQ,否則將它壓入記憶體時間輪中,時間輪中到了預期投遞時間,再投遞到業務MQ。再計算下次觸發時間戳為1612493465,將redis 中的資料修改為:

JobId

1612493465

執行器上的本輪定時任務處理完畢後1,處理下一輪:拉取到1612493465 這一次時間戳,隨後重複上面的邏輯,或者傳送MQ 訊息或者壓入時間輪,依次往下。

3.2 固定延遲型別

使用者建立了一個固定延遲型別的任務,再15s鍾之後執行,攜帶的引數如下

FixDelay 15000

其它透傳引數

作業管理服務先投遞到MQ 普通訊息。消費者持久化該條資料,獲得主鍵jobpk1,計算出來該定時任務後續的觸發時間戳為1612493475,儲存到觸發器的儲存中,必要欄位為:

JobId

1612493475

執行器上由cloudjob 排程的定時任務在做掃描時,掃描到了該條觸發器資料,判斷是否到了預期投遞時間,如果已經到了直接投遞到業務MQ,否則將它壓入記憶體時間輪中,時間輪中到了預期投遞時間,再投遞到業務MQ。由於是固定延遲,沒有下次執行,將redis 中的資料修改為:

JobId

0

執行器上的本輪定時任務處理完畢後,處理下一輪:拉取的時間戳要大於0,則這個作業以後不會被掃描到。在非同步記錄任務時,會將該redis 中為0 的這個member 刪除,並把元資料該作業的狀態設定為完結。

4. 服務部署及實施流程

通過前面的介紹大家知道了CloudJob的工作原理,下面通過幾個模組的部署來說明一下具體的實施過程。

4.1 作業初始化

業務的作業通過作業管理服務介面批量註冊。作業管理接收到請求後傳送到MQ 普通訊息,由消費者完成如下步驟:

由於作業總數百萬級別,需要將作業劃分到不同分片上,每一個作業在註冊進來時需要

獲取到尚未飽和的分片。分片的數量是由執行器負載服務管理的。假設一個分片的負載容量為1萬,在分片承載的作業沒達到1萬之前,作業都可以被分到這個分片上。如果達到這個閾值,分片被設定為飽和狀態,需要分配新的分片,新的作業將被分配到新的分片上。具體過程如下:

假設新增一個5s 執行一次的作業時,獲取到了sharding1 分片,將會在DB 中儲存元資訊同時快取元資訊。資料為

主鍵

時間表達式

分片

版本

JobId

CRON 0 0/5 * * * ?*

Sharding1

0

Version0 表示該作業元資訊是首次存入,以後每修改一次這個version 遞增。計算下次觸發時間並在觸發器快取中存入如下資料:zset 名稱sharding1,member 為jobpk1_0,是作業主鍵和version 的組合,可以採用高位存主鍵,地位存版本號的方式。score 為下次觸發時間戳1612493460。

4.2 執行器高可用

執行器會定時掃描mongodb資料庫,從資料庫中獲取符合條件的任務,這個定時任務是由Elastic-Job來分配執行的,Elastic-Job將分片分配到了各個執行器中,假設是下面這種分配模型:兩個執行器均分了兩個分片,執行器1只處理具有分片屬性sharding1的觸發器,執行器2同理。當執行器1出現宕機時,Elastic-Job將會觸發失效轉移,分片1將會分配給執行器2,此時執行器2會有兩個執行緒分別處理分片1 和分片2。如果執行器後面啟動成功,Elastic-Job將會重新分片,兩個執行器又會均分分片。

4.3 執行器執行緒模型

執行器在執行時內部有兩種執行緒,一個是定時任務掃描執行緒,另一個是訊息佇列消費執行緒,它們的工作模型如下:

定時任務掃描執行緒:主要負責定時拉取觸發器,隨後均衡投遞到對應時間輪中,一個時間輪由一個工作執行緒負責處理。如果出現工作執行緒處理時間輪中作業比較慢,出現大量堆積的情況,需要將對應分片屬性設定為飽和狀態,此時不會有新的作業被分配到該執行器,直到該分片重新恢復為非飽和狀態。

訊息佇列消費執行緒:主要負責處理定時任務無法cover 到的馬上要執行的觸發訊息,比如定時任務的處理週期是20s,現在使用者提交了一個每隔10s執行一次的任務,這個時候系統就需要生成一個10s之後執行的觸發訊息並把它寫入到訊息佇列中,執行器的消費執行緒就可以立即得到這個觸發訊息,及時加入到執行器的時間輪中,確保訊息能夠按時執行。

5. CloudJob使用實踐

在CloudJob分散式任務排程平臺搭建好以後,使用者就可以將任務部署到這個平臺上。下面介紹一下使用者在使用CloudJob過程中遇到的問題和優化方案。

5.1 叢集隔離

相比與其它分散式任務排程框架,CloudJob是一個“重量級”的排程平臺,搭建一套CloudJob需要MongoDB資料庫、redis叢集,訊息佇列以及多臺主機作為執行器,如果使用者的任務量非常少,這將會導致資源的浪費。所以可以採用叢集隔離的方法,將各個部門的使用者作業部署到一個CloudJob叢集中,同時採用一種隔離方式讓使用者的作業互不影響,這樣就可以合理的利用資源。

叢集隔離的具體思路是先搭建一套物理叢集,使用者在建立作業之前需要先建立一個邏輯叢集(或者選擇之前已經建立好的邏輯叢集),在這個邏輯叢集設定限流策略、超時機制,並與物理叢集關聯起來,使用者之後將自己的任務註冊到這個邏輯叢集中,任務就可以被物理叢集排程,任務的限流策略、超時機制等又是以邏輯叢集為單位管理的,這樣就實現了叢集隔離。

5.2 鏈路追蹤與作業歷史視覺化

在CloudJob叢集中,一個任務從註冊到最終執行需要經過多個處理流程,為了便於排查問題和進行作業歷史統計,平臺需要對任務流轉的各個階段進行鏈路追蹤,同時需要將監控資料進行持久化,便於查詢作業的歷史記錄。鏈路追蹤與作業歷史視覺化的框架如下:

鏈路追蹤的方案是通過埋點的方式,對系統中的主要處理流程進行記錄,系統中的模組每進行一次處理就生成一條記錄資料,記錄資料通過訊息佇列定時傳送到任務監控平臺,任務監控平臺將這些資料處理後儲存到Elasticsearch中,為後續的統計、查詢做準備。

同時平臺提供了前端頁面給使用者進行作業歷史視覺化,使用者可以在頁面上檢視作業的歷史記錄,檢視每一次執行的排程情況,通過頁面還可以檢視到作業是否有漏發、嚴重超時。

6. 總結與展望

CloudJob作為一個高效能、低延遲的分散式任務排程平臺,通過將任務劃分到不同的分片、每個執行器處理對應分片的任務,實現了系統的動態擴充套件和擁有海量任務排程的能力;使用定時任務掃描、時間輪、系統內部的訊息佇列來保證任務及時觸發,保證了任務執行的低延遲;同時CloudJob通過Elastic-Job來執行系統內部的定時任務,保證執行器的高可用。

當然CloudJob還是有些不足,如果使用者將任務部署到CloudJob平臺上,還需要將自己的業務處理程式碼執行到自己的主機上,這會造成主機資源的浪費。後續CloudJob的演進方向就是將任務平臺接入到Serverless平臺,使用者只需要在頁面編輯自己的業務程式碼,然後點選儲存,平臺就會在Serverless中新建任務例項,將使用者的程式碼執行在任務例項中等待接收觸發訊息,執行完任務後自動釋放任務例項。這樣既可以方便使用者快速部署任務,又可以充分利用資源。

作者簡介

Xinchun  OPPO高階後端工程師

目前負責分散式作業排程系統的開發,關注訊息佇列、redis資料庫、ElasticSearch等中介軟體技術

推薦閱讀

|MySQL 分散式事務的“路”與“坑”

|OPPO大資料離線任務排程系統OFLOW

本文版權歸OPPO公司所有,如需轉載請在後臺留言聯絡