承 林 王海寧 高春成
(南瑞集團有限公司 江蘇 南京 211000)(北京科東電力控制系統有限責任公司 北京 100192)
隨著電力改革的推進,電力交易規模的不斷擴大,參與電力市場的各類用戶快速增長,電力市場也正在從中長期市場向現貨市場逐步擴展。這些都對系統架構、并發響應能力、系統資源分布式調度使用能力、大數據并行計算能力提出了更新更高的要求。因此借助云計算技術、服務化設計思想,對傳統單體架構的電力交易系統進行改造已成為一種必然趨勢[1-3]。
任務調度[4-5]作為信息系統中最重要的功能之一,是管理信息系統中各種任務優先級安排以及任務執行的中樞。任務調度算法一般分為事件驅動調度算法、時鐘驅動調度算法[6]。目前,時鐘驅動調度算法在電力交易系統平臺[7-8]中有著廣泛的應用。例如橫向數據同步、縱向數據同步、電力交易日清算、電力交易月結算、交易對賬、日志輪轉、定期數據統計分析、定期數據校驗等功能,這些都屬于時鐘驅動調度算法的任務調度處理方式。然而在分布式微服務化的信息系統架構中,不僅存在著基于時鐘驅動調度算法的業務處理需求,在各微服務之間的數據請求、數據處理的信息交互下,基于事件驅動調度算法的任務調度也將普遍存在。從資源分布、調度算法、任務執行控制、數據一致性等方面看,傳統的單體系統架構中任務調度框架已無法適應分布式系統架構中對任務調度的要求。在這種情況下,一些互聯網企業基于自身的需求開發出了一系列分布式調度系統,如淘寶網的TBschedule和當當網的Elastic-Job等技術架構,但是這些系統應對業務規模和基礎設施與電力交易系統存在的差異較大,關注的問題也往往在負載均衡上。直接將這些系統架構應用于集群規模小,任務絕對量少,策略復雜電力交易系統中并不適合,因此急需一種針對電力交易業務應用特點和分布式系統架構的任務調度解決方案[9-10]。
在傳統的單體結構中,單機任務調度獲得廣泛應用,操作系統和各種語言的調用庫,都提供了良好的實現機制。當前基于F5負載均衡的多節點電力交易系統中,仍采用單體調度任務模式,其中:有的調度任務部署在一個節點;有的調度任務部署在多個節點同時重復執行。前者容易出現單點故障,一旦配置調度任務的節點宕機,將會導致整個任務調度的失效;后者不僅增加無效的負載,而且容易出現數據一致性的問題。
通過將函數封外部接口、輪訓方式調用或者利用虛擬IP實現主備機,實現一些簡單分布式任務調度的能力,本質上仍是將分布式任務調度轉化為單機任務調度問題。這不僅容易出現單點故障,而且對復雜業務的調度任務分配也難以應對,調度任務需要手動注冊在每一個節點上,配置和維護也十分繁瑣。
隨著電力交易系統業務的擴展和系統架構向云計算、微服務方向演進,任務調度的場景也將日益復雜化。歸納起來,可以考慮如下三種場景:場景A:任務一執行失敗,寫入部分數據,任務二讀取到任務一產生的臟數據導致不一致。場景B:任務一先于任務二執行,而任務二先于任務一完成,舊數據覆蓋新數據同樣導致數據不一致問題。場景C:執行任務節點異常,系統未能成功喚起其他節點執行。
在實踐中,無論是單機調度還是分布式調度,對于事物的控制通常由業務邏輯本身支持,不同業務調度之間通常是業務數據依賴。場景A和場景B中,數據一致性主要是同一調度任務不同批次之間的數據一致性。對于需用各臺機器執行相同的任務,本質上屬于單機調度的范疇。對于主控性任務,需要多臺機器中選出一臺執行的任務。如果只在一臺機器上執行,那么此時分布式調度也退化成單機調度。對于場景C在不考慮分布式事物情況下,可以視為主備問題。
解決分布式系統中任務調度問題,通常有調度集中式控制和分布式規劃控制式兩種。
(1) 集中式控制 是任務的集中觸發控制,由獨立的控制模塊控制,各個節點只提供任務觸發的接口。
(2) 分布式控制 有各個節點獨立的維護任務觸發邏輯,控制中心只起到協調的作用。
集中式控制是出現較早也容易實現的方式,但容易出現單點故障。比如基于虛擬IP進行輪詢就是一種簡單實現,但虛擬IP失效時會引起任務調度系統整體失效。在單機任務調度應用廣泛的Quartz框架也基于數據庫行鎖機制提供了一套分布式解決方案,但是仍然無法避免單點故障問題。
分布式控制將任務調度控制權分擔到各個節點上,來避免單點故障問題。但是這種設計引入了復雜性,需要解決分布式系統中的協調問題。淘寶網的TBschedule和當當網的Elastic-Job也主要是通過引入Zookeeper技術來進行解決。
(1) Zookeeper與Leader選舉 Zookeeper[11-14]是一個分布式的協調工具,通過zab算法來解決分布式系統一致性問題。Zookeeper分布式系統中解決統一配置、分布式命名空間、分布式隊列、Leader選舉等功能。
在分布式系統中,Leader選舉是在一個跨越幾臺機器(節點)的分布式任務中,指定一臺機器作為任務組織者,在選舉進行之前各個節點并不知道哪臺機器將會成為Leader。在Leader選舉之后各臺機器都將知道集群中唯一的Leader。因為Zookeeper保證節點之間的數據一致性和順序性,使用Zookeeper可以滿足Leader選舉的要求。創建一個節點election通知相關機器參與選舉,各臺機器接到通知后在election節點下方建立順序臨時節點,然后選取序列號最小的節點作為Leader。Leader選舉結束后,對Leader進行監聽,一旦發現Leader節點被刪除,重新發起Leader選舉。但當Leader失去時,所有節點就會同時拉取election節點下的所有子節點,來重新進行選舉。這就會對Zookeeper集群產生很大的壓力。一種改進方法就是:任何節點只監聽下一個兄弟節點,一旦出現Leader失效,監聽Leader的節點必然成為Leader,因為沒有序號比它更小。
(2) 任務分配策略考慮 傳統任務分配策略類似操作系統的作業調度,主要解決同構任務在不同節點的分配,關心任務執行的效率和負載均衡問題。比如在分布式計算中,子計算任務的分配策略側重考慮的是各個節點的CPU、內存等資源如何得到充分的利用以及如何在任務失敗后重新分配。
對電力交易系統來說,分配策略的復雜性在于分配策略的多樣性。一種任務分配策略是各個節點都需要同時執行的,如定時拉取緩存,訪問數據庫;另一種是互斥執行的,如定時結算等,這種需要在數個節點中選出一個執行,屬于一種互斥性任務。
對于涉及的節點數目較少,不需過分考慮各個節點之間的均衡問題,只保證不出現單點故障的情況,本文采用Leader選舉方式解決互斥問題。當Leader失去服務能力時候,進行Leader切換,互斥任務也遷移到新的Leader上,形成主從備份。
對于任務失敗的處理策略,各種任務也不同,有些任務需要重復執行,有些需要任務放棄執行。各種任務失敗策略以配置形式進行注冊,以滿足各種任務的需要。
(3) 腦裂問題的預防和解決思路 在實際的生產環境中,網絡震蕩是隨時可能出現的,如果Leader所在機器出現短暫網絡中斷,集群則會認為Leader已經宕機,從而重新發起Leader選舉。舊的Leader本身并不知道集群已經產生了新Leader,這種情況常被稱為腦裂。雖然Zookeeper本身保證了腦裂問題不會長期出現,但是需要舊Leader等待集群的一個通知。在(2)中介紹的互斥任務是在Leader上進行執行的,即使短暫腦裂,也可能引起任務重復執行,對于計費、清算這種業務來說這是不可接受。所以Leader需要對網絡事件進行監聽,一旦產生網絡中斷,立即釋放Leader,同時重新進行選舉時候,等待一定的時間間隔,保證失去網絡的Leader完成釋放。
(4) 任務的觸發控制 分布式調度任務觸發控制可以分為時鐘控制和任務觸發兩部分。
時鐘觸發:通過時間滿足時鐘條件時,激活相關動作。激活條件可以使用類似Crotnab的時間表達式注冊于任務調度中心,表達式采用字符形式表達時間條件,每個字符分別表達秒、分鐘、小時、日、月、星期、年等。這種表達式可以清晰表達時間條件。
任務控制:任務的方法、類、執行對象通過字符串的形式進行注冊,滿足時鐘條件的時候,通過反射技術進行調用。在Java語言中提供了原生的反射功能支持。另外,SpingTask[15]和Quartz[16]也提供完整時鐘機制和反射調用框架[17],并且容易同基于Spring架構的電力交易系統進行集成,降低了實用和開發的難度。
電力市場交易邏輯復雜,調度任務場景類型多,特別是分布式服務化架構的引入,使得電力交易系統對分布式任務調度存在較迫切的應用需求。
本文依據電力交易系統的應用場景特點、現有技術架構特點以及分布式架構的技術要求,并參考互聯網行業的成熟解決方案,提出一個基于分布式控制的任務調度解決方案。其架構如圖1所示。

圖1 分布式調度方案架構圖
該方案主要由管理界面、任務調度中心、任務調度客戶端三個部分組成。用戶界面提供給用戶注冊、編輯任務功能;任務調度中心對任務分配進行管理;任務調度客戶端負責任務觸發和執行。
(1) 管理界面 管理界面提供一個可視化的交互平臺,提供調度任務注冊、暫停、取消等功能,監控任務的執行的結果。
任務注冊:任務執行是將所在機器(節點)具體指定的類和方法,以及任務執行的Crontab時間表達式注冊于系統。對于注冊于多臺機器(節點)的任務,還需指定是互斥任務還是并發任務?;コ馊蝿諡樵跐M足任務條件時選出一臺機器(節點)執行任務,其他機器作為備份;并發任務為這些機器(節點)同時執行的任務。
任務修改:管理界面提供功能啟動和暫停任務,可以在任務執行時刻前暫停任務。
任務監控:通過管理界面查看任務的狀態、歷史執行時間、執行結果、互斥任務顯示、執行的實際機器等信息。
(2) 任務調度中心 如圖2所示,任務調度中心為調度系統核心,控制各個節點任務的實際執行行為。本文提出的調度中心使用基于Zookeeper的方案,用戶通過管理界面注冊任務時,是注冊在各個機器對應的節點下面,并在該節點下面建立子節點??蛻粜薷娜蝿諘r,調度中心就更新對應節點下面的內容。管理端刪除任務時候,將對應任務節點刪除。

圖2 任務調度中心
(3) 任務調度客戶端 任務調度客戶端部署執行的應用,客戶端部分負責調度的具體執行,客戶端的主要結構有監聽器、任務容器、任務調度器,如圖3所示。

圖3 任務客戶端示意圖
監聽器在客戶端所在應用進程初始化開始時對調度中心對應的IP節點開始監聽,并從該節點拉取任務數據保存到任務容器中。如果子節點發生增加、刪除、修改就要對任務容器中對應的任務信息進行增加、刪除、修改,并且通知任務控制器進行相應的處理。
任務容器:用來存儲本地任務的具體信息,如任務名稱、任務類型、任務調度方法、調度表達式、任務狀態等信息。
任務調度器:初始化時任務調度從任務容器獲取任務,依據任務的時間表達式來啟動任務,并將執行狀態寫到任務容器中。當監聽器監測到新增或者啟動事件時,調度器將會從任務容器中取出任務,檢查任務狀態,如果任務尚未啟動就啟動它,并更新狀態到任務容器;如果任務執行失敗,則根據配置判斷是否需要重復執行。
如果監聽到暫?;蛘邉h除事件,將首先修改狀態,再將其從任務容器中刪除。
如果任務的類型是互斥任務,客戶端初始化時,就會發起Leader選舉,從多臺備用機中選出一臺,作為實際執行調度的機器。調度器在加載任務時,如果檢查到自身是Leader就正常啟動,如果不是Leader就放棄啟動。任務啟動后Leader選舉的狀態保持監聽,如果監聽到喪失Leader權限,就暫停任務,如果監聽到獲得Leader身份,則重啟動調度任務。
(4) 效果監控功能 為了調度任務進行管理和監控,任務的注冊、執行、完成或者異常等數據將實時寫入消息總線,并通過消息總線同步到數據庫中。管理界面通過發布訂閱機制與數據總線保持監聽,并將接收到的消息實時同步到管理界面。
監控模塊對寫入消息總線的數據進行過濾,當監控到任務異常消息時觸發報警通知管理員處理。由于本文闡釋系統不考慮對分布式事物的控制,即使在單機系統的調度中,調度系統本身也不牽涉到回滾等事物操作的,所以在實踐中采用報警觸發的機制是能滿足實際業務需要的。
基于本文提出的方案構建的系統,在實踐中運行了三個月,其中普通節點4臺,執行主控性任務機器2臺,任務執行的成功率為99.98%,未發生因Leader切換失敗或任務失效等情況,也未發生因短期腦裂產生任務重復執行的情況。
對于電力交易系統而言,負載性能并不是業務痛點所在。為應對更大規模的集群介入,對該系統進行了壓力測試,結果如表1所示。

表1 壓力測試結果
本系統采用分布式的技術架構,對于任務調用中心的Zookeeper,可以通過擴充集群來提高負載性能,對于管理端界面,也可以通過負載均衡的手段,實現水平擴展提高吞吐量。
在系統運行的幾個月中,除了系統上線引起Leader切換外,因網絡問題出現幾次意外的切換,但都通過本文的Leader選舉改進機制和任務注冊管理功能,防止了分布式系統中Leader選舉的腦裂問題,從而保護了任務不會因為網絡震蕩被重復調度執行。
本文提出了一種基于改進式Leader選舉的分布式任務調度系統,解決了電力交易系統從單體架構演進到分布式架構中的復雜任務調度問題。在分析了電力交易業務和電力交易系統的基礎上,利用改進式Leader選舉方式解決了互斥任務的調度問題,提供了可配置的失敗任務處理方式。為多樣性的電力交易系統提供了靈活的支持,并通過方案驗證和壓力測試,證明該方案不僅能夠滿足當前系統的需要,而且在面對更大規模業務需要時,依然能夠良好運行。