奇富科技(原360數科)是人工智慧驅動的信貸科技服務平臺,致力於憑藉智能服務、AI研究及應用、安全科技,賦能金融機構提質增效,助推普惠金融高質量發展,讓更多人享受到安全便捷的金融科技服務。作為國內領先的信貸科技服務品牌,累計註冊用戶數2億多。 奇富科技之前使用的是自研的任務調度框架,基於Python ...
奇富科技(原360數科)是人工智慧驅動的信貸科技服務平臺,致力於憑藉智能服務、AI研究及應用、安全科技,賦能金融機構提質增效,助推普惠金融高質量發展,讓更多人享受到安全便捷的金融科技服務。作為國內領先的信貸科技服務品牌,累計註冊用戶數2億多。
奇富科技之前使用的是自研的任務調度框架,基於Python研發的,經常面臨著調度不穩定的狀況,難以維護。後來引入了Apache DolphinScheduler作為公司的大數據任務調度系統,面對大量任務調度的考驗,經歷了半年磨合期,目前Apache DolphinScheduler在奇富科技運行非常穩定。本文將介紹該公司團隊最近一年在開源版Apache DolphinScheduler基礎上所做的優化和改進。
一、技術架構
在我們公司的大數據離線任務調度架構中,調度平臺處於中間層。用戶通過數據集成平臺提交數據同步任務給調度平臺,通過數據開發平臺提交工作流給調度平臺。用戶不和調度平臺直接交互,而是和數據集成平臺和數據開發平臺交互(圖1)。
由於我們是一個金融相關業務的公司,業務需要保證高可用。因此,我們的調度平臺是異地雙機房架構,核心工作流會異地雙機房運行。集群角色分為cluster A和cluster B,其中cluster A為主集群,cluster B為從集群(圖2)。用戶的工作流在A集群運行,其中核心關鍵工作流會在A和B集群雙機房運行。以下是調度集群各服務個數。其中Api、Alter、Master服務在虛擬機部署,Worker和Logger部署在物理機上。
二、業務挑戰
01 調度任務量大
我們目前每天調度的工作流實例在3萬多,任務實例在14萬多。每天調度的任務量非常龐大,要保障這麼多任務實例穩定、無延遲運行,是一個非常大的挑戰2
02 運維複雜
因為每天調度的任務實例非常多,我們經歷了幾次調度機器擴容階段。目前2個調度集群有6台Master、34台Worker機器。而且調度機器處於異地2個城市,增加了很多管理運維複雜性。
03 SLA要求高
因為我們業務的金融屬性,如果調度服務穩定性出問題,導致任務重覆調度、漏調度或者異常,損失會非常大。
三、調度優化實踐
我們在過去一年,對於調度服務穩定,我們做瞭如下2個方向的優化。第一,調度服務穩定性優化。第二、調度服務監控。
01 重覆調度
在2023年初,用戶大規模遷移工作流時,遇到了工作流重覆調度問題。該問題,現象是同一個工作流會在同一個集群同一時間,生成2個工作流實例。經過排查,是因為用戶在遷移時,會有工作流遷移項目的需求,比如從A項目遷移到B項目。在工作流上線時,用戶通過提交工單,修改了調度資料庫中工作流的項目ID,進行遷移。這麼做會導致該工作流所對應的quartz元數據產生2條數據,進而導致該工作流重覆調度。如圖3所示,JOB_NAME為’job_1270’的記錄,有2條數據,而JOB_GROUP不一樣。查詢源碼job_name對應工作流的定時器ID,JOB_GROUP對應項目ID。因此修改工作流對應的項目ID,會導致quartz數據重覆和重覆調度。正確遷移工作流項目的方式是,先下線工作流,然後再修改項目ID。
如何避免和監控此問題,我們根據這個邏輯,寫了重覆調度的監控sql,在最近一年中,數次提前發現了quartz的漏調度問題。
SELECT count(1)FROM (SELECT TRIGGER_NAME, count(1) AS num FROM QRTZ_TRIGGERS GROUP BY TRIGGER_NAME HAVING num > 1 )t
02 漏調度
在2023年初,在凌晨2點,有些工作流發生漏調度,我們排查後發現是凌晨2點0分調度太集中,調度不過來。因此我們優化了quartz參數,將org.quartz.jobStore.misfireThreshold從60000
調整為600000。
如何監控和避免此問題,監控sql摘要如下:
select TRIGGER_NAME,NEXT_FIRE_TIME ,PREV_FIRE_TIME,NEXT_FIRE_TIME-PREV_FIRE_TIMEfrom QRTZ_TRIGGERSwhere NEXT_FIRE_TIME-PREV_FIRE_TIME=86400000*2
原理就是根據quartz的元數據表QRTZ_TRIGGERS
的上一次調度時間PREV_FIRE_TIME
和下一次調度時間NEXT_FIRE_TIME
的差值進行監控。如果差值為24小時就正常,如果差值為48小時,就說明出現了漏調度。
如果已經發生了漏調度如何緊急處理? 我們實現了漏調度補數邏輯通過自定義工作流進行http介面調用。如果監控到發生了漏調度情況,可以立即運行此工作流,就能把漏調度的工作流立即調度運行起來。
03 Worker服務卡死
這個現象是凌晨調度Worker所在機器記憶體占用飆升至90%多,服務卡死。
我們思考產生該問題的原因是,調度worker判斷本機剩餘記憶體時,有漏洞。比如我們設置worker服務剩餘記憶體為25G時,不進行任務調度。但是,當worker本機剩餘記憶體為26G時,服務判斷本機剩餘記憶體未達到限制條件,那麼開始從zk隊列中抓取任務,每次抓取10個。而每個spark的driver占用2G記憶體,那麼本地抓取的10個任務在未來的記憶體占用為20G。我們可以簡單計算得出本機剩餘記憶體為26G-20G為6G,也就是說抓取了10個任務,未來的剩餘記憶體可能為6G,會面臨嚴重不足。
為瞭解決這個問題,我們參考Yarn,提出了”預申請”機制。預申請的機制是,判斷本機剩餘記憶體時,會減去抓取任務的記憶體,而不是簡單判斷本機剩餘記憶體。
如何獲取將要抓取任務的記憶體數呢? 有2種方式,第一種是在創建工作流時指定本任務driver占用的記憶體,第二種是給一個固定平均值。
我們綜合考慮,採用了第二種方式,因為對於用戶來說,是沒有感知的。我們對要抓取的每個任務配置1.5G(經驗值)記憶體,以及達到1.5G記憶體所需要的時間為180秒,抓取任務後,會放入緩存中,緩存過期時間為180(經驗值)秒。剩餘記憶體計算公式,本機剩餘記憶體=本機真實物理剩餘記憶體-緩存中任務個數1.5G+本次準備抓取的任務數1.5G 。
還是同樣的場景,本機配置的剩餘記憶體為25G,本機實際剩餘記憶體為26G,要抓取的任務為10個。每個任務未來占用的driver記憶體為1.5G。簡單計算一下,本機剩餘記憶體=26G-10*1.5G。在“預申請”機制下,本機剩餘記憶體為1G,小於25G,不會抓取,也就不會導致Worker機器的記憶體占用過高。那麼會不會導致Worker服務記憶體使用率過低呢,比如shell、python、DataX等占用記憶體低的任務。結論是不會,因為我們有180秒過期機制,過期後,計算得到的本機剩餘記憶體為變高。
根據同樣的原理,CPU占用,我們也加上了同樣的機制,給每個要抓取的任務分配一定的cpu負載值。
加上記憶體預申請後,最近半年,沒有遇到由於記憶體占用過高導致worker服務卡死的問題。以下是我們加上記憶體預申請機制後,worker記憶體使用率情況,可以看見worker最大記憶體使用率始終穩定保持在80%以下。
04 任務重覆運行
在worker服務卡死時,我們發現yarn上的任務沒有被殺死,而master容錯時導致任務被重覆提交到yarn上,最終導致用戶的數據異常。
我們分析後發現,任務實例有一個app_link欄位,存放用戶提交的yarn任務的app id,而第一次調度的任務的app id為空。排查代碼發現worker在運行任務時,只有完成的yarn 任務,才會更新app_link欄位。這樣導致master在容錯時,拿不到app id,導致舊任務沒有被殺死,最終導致任務重覆提交。
我們進行的第一個改進點為,在worker運行yarn任務時,從log中實時過濾出app id,然後每隔5秒將app id更新到app_link欄位中。 這樣yarn任務在運行時,也就能獲取到app id,master容錯時就能殺死舊任務。
第二個改進點為,在worker服務卡死從而自殺時,殺死本機上正在運行的調度服務,這樣可能master就不需要進行容錯了。
實施這個方案後,最近半年沒有遇到重覆調度的yarn任務了。
05 弱依賴
運營標簽對於時效性要求很高,關係到廣告投放效果。他們提出了一個需求,他們對於某些依賴工作流,不是強依賴的,如果該父工作流在約定的時間沒有完成,那麼就不進行依賴。為了實現這個需求,我們引入了弱依賴的機制。舊依賴模式,我們定義為強依賴,如果該工作流在約定周期沒有運行完成,那麼永遠不能依賴成功。而弱依賴,會等待到某個時間,如果還沒有完成,那麼也會成功。
06 虛擬節點
我們調度集群是雙機房運行的,因此有些核心工作流是運行在2個機房的。比如有些數倉ads相關工作流是輸出hive數據到mysql表的,而mysql數據源來不及雙數據源,只有一個mysql。因此主集群導入數據到mysql表,從集群就不應該導入數據到mysql表中。因此我們實現了虛擬節點的功能,實現的目標是,此節點在主集群真實運行,在從集群虛擬運行。
07 任務的yarn隊列動態切換
我們的yarn隊列是根據大業務線進行劃分的,隊列個數並不多。我們對於用戶的調度任務穩定性需要保障,而經常需要到的一個情況是,yarn的隊列經常被補數任務占用過多,導致用戶正常的調度任務提交不上去。
因此,我們提出了任務的yarn隊列動態切換方案。 原理就是當用戶補數時,數據開發平臺根據用戶所屬業務線,找到該用戶所屬的yarn隊列名稱,然後將該隊列名稱提交到全局變數中。調度worker在對該任務進行調度時,會判斷該全局變數是否有值,如果有就進行替換。
通過該方案,我們實現了調度任務在正常隊列中運行,而補數任務進入補數的小隊列中運行。從而保證了正常調度任務的時效性和穩定性。
08 實例分頁查詢介面優化
每天調度的任務實例有14萬多,我們保留了2個月數據,那麼任務實例的記錄數約為1000多萬條。而DolphinScheduler查詢工作流實例和任務實例有join關係,需要通過join查詢project_id,在查詢一些大的項目的任務實例時,耗時最大為幾分鐘甚至直接卡死。
我們提出的解決方案是,通過欄位冗餘,在工作流實例和任務實例中存儲project_id,將join分頁查詢改為單表分頁查詢。 優化後,大項目的任務實例分頁查詢p99耗時從幾分鐘降低到200ms。
09 Worker維護模式
在worker發版時,我們不應該影響用戶調度的任務。因此,我們實現了worker的維護模式。當worker開啟維護模式時,該worker不會再新抓取任務,而已經抓取的任務繼續運行,從而不影響用戶的調度任務。過4小時後,判斷該worker上任務運行完成,再對該worker進行jar包替換和重啟服務。通過這種方式,我們能夠做到DolphinScheduler發版對用戶的調度任務無影響,用戶無感知。
10 worker和nodemanager混部
隨著業務發展,公司每天調度的工作流實例越來越多,worker服務經常記憶體不足,需要申請大記憶體的機器作為worker調度機。不過,面臨著降本增效的壓力,我們思考DolphinScheduler的worker服務能不能和yarn的nodemanager進行混合部署,因為我們的yarn集群有1000多台機器。我們希望通過這種方式達到不用申請新的機器,從而降低成本的目標。
我們的解決方案如下,新擴容worker服務在nodemanager上,在晚上23點,通過yarn命令將該混部的nodemanager可用記憶體調低為1核4G,從而停止yarn將任務調度到該機器上,然後調用api介面,關閉該worker的維護模式,讓該worker調度ds分配的任務。在早上10點,通過調用api介面,打開worker的維護模式,從而停止worker調度ds分配的任務,並通過yarn命令將nodemanager的記憶體和cpu恢復為正常值,從而讓yarn分配任務到該機器上。
通過這種方案,我們實現了凌晨該機器給DolphinScheduler的worker使用,白天給yarn的nodemanager使用,從而達到降本增效的目標。 新擴容的worker,我們都採用了這種方式。
四、服務監控
一個穩定的系統,除了代碼上的優化,一定離不開完善的監控。而DolphinScheduler服務在每天調度這麼大量時,我們作為開發和運維人員需要提前知道調度系統和任務健康狀況。因此根據我們的經驗,我們在DolphinScheduler服務的監控方向做瞭如下事情。
01 方法耗時監控
我們通過byte-buddy、micrometer等,實現了自定義輕量級java agent框架。這個框架實現的目標是監控java方法的最大耗時、平均耗時、qps、服務的jvm健康狀況等。並把這些監控指標通過http暴露出來,然後通過prometheus抓取,最後通過grafana進行展示,並根據prometheus指標進行告警。以下是master訪問zk和quartz的最大耗時,平均耗時,qps等。
以下是master服務的jvm監控指標
通過該java agent,我們做到了api、master、worekr、zookeeper等服務方法耗時監控,多次提前發現問題,避免將問題擴大到用戶感知的狀況。
02 任務調度鏈路監控
為了保障調度任務的穩定性,有必要對任務調度的生命周期進行監控。我們知道DolphinScheduler服務調度任務的全流程是先從quartz中產生command,然後從command到工作流實例,又從工作流實例再到任務實例。我們就對這個任務鏈路進行生命周期監控。
1)監控quartz元數據
前面已經講了我們通過監控quartz元數據,發現漏調度和重覆調度問題。
2)監控command表積壓情況
通過監控command表積壓情況,從而監控master是否服務正常,以及master服務的性能是否能夠滿足需求。
3)監控任務實例
通過監控任務實例等待提交時間,從而監控worker服務是否正常,以及worker服務的性能是否能夠滿足需求。
通過如上全生命周期監控,我們多次提前發現worker服務的性能問題,提前解決,成功避免影響到用戶調度服務。
03 日誌監控
前面我們通過java agent實現了方法耗時的監控,不過這還不夠。因此,我們還通過filebeat採集了3台api、6台master、34台worker的服務日誌到我們公司的日誌中心,然後對日誌進行異常突增告警。
五、用戶收益
通過最近一年對DolphinScheduler代碼的優化,我們獲得的最大收益是近半年沒有因為調度服務導致用戶的SLA受影響,並多次在調度服務出現問題時,提前解決,沒有影響到用戶任務的SLA達成率。
六、用戶簡介
圖片
奇富科技(原360數科)是人工智慧驅動的信貸科技服務平臺,秉承“始於安全、 恆於科技”的初心,憑藉智能服務、AI研究及應用、安全科技,賦能金融機構提質增效,助推普惠金融高質量發展,讓更多人享受到安全便捷的金融科技服務,助力實現共同富裕。作為國內領先的信貸科技服務品牌,累計註冊用戶數2億多。
作者介紹
- 劉坤元
奇富科技數據平臺部大數據開發工程師,19年入職奇富科技,目前負責大數據任務調度系統開發和任務治理工作。
- 王潔
奇富科技數據平臺部大數據開發工程師,19年入職奇富科技,目前負責大數據任務調度系統開發工作。
本文由 白鯨開源 提供發佈支持!