奇富科技(原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年入職奇富科技,目前負責大數據任務調度系統開發工作。
本文由 白鯨開源科技 提供發佈支持!