一.? pipeline任務描述
? ? ? ? 在執行某序列化任務的時候,通常可將任務劃分為多個stage分別進行模塊化,每一個stage可抽象為一個處理方法、一個處理類或一個處理模塊,最簡單一個的流程任務(無分支),如下圖:
? ? ? ? ? 對于一個完整的處理流程有如下說明:
? ? ? ? ? 1.? 每個處理單元具有業務處理原子性特征,每個處理單元僅處理單一的功能需求
? ? ? ? ? 2.? 每個處理單元可能有多個來自其他單元的輸入數據,也可以向多個其他單元輸出數據
? ? ? ? ? 3.? 某單元的輸入可依賴于上一個單元或多個單元的輸出,需等待所有的前置單元都處理完才能執行此單元
二.? 可能的執行場景
? ? ? ?基于以上說明,可以將pipeline的流程簡單概括為一下幾種可能的執行場景:
????樣例01表示最簡單的流線式模塊處理,無分支,前一個節點處理結束再處理下一個節點。
????樣例02表示單個節點可能有0到N個輸入,也可能有1到N個輸出;同時,單節點的多個輸入可來自不同的節點的任意輸出或單節點的多個輸出數據流向不同的后置節點。
? ???樣例03表示pipeline的處理流程節點不是一棵簡單的樹形結構,可能存在多個根節點。
三.? 利用JSON對pipeline進行結構描述
? ? ? 如何設計一種通用的數據結構來描述一個pipeline,既可以實現以上樣例的幾種場景,也可以實現其他更復雜的pipeline場景。設計要點:
? ? ? 1. 對pipline中每個抽象節點的實例化描述
? ? ? ?2. 對pipeline中每個節點的模塊參數的描述(即節點執行需要的配置參數,而非input端口數據)
? ? ? ?3.?對pipeline中每個節點的輸入輸入端口的描述
? ? ? ?4.? 對pipeline中每個節點的對其他節點輸入輸入端口依賴關系的描述
????最終的實現方式如下(JSON數據):
? ? 其中包括以下幾點:
? ? 1.? pipeline_id 每個pipeline都有一個唯一的ID,多個pipeline同時獨立運行時會用到
? ? 2.? pipeline_items是一個數組,里面每一個大括號都是一個特定類型的模塊實例化時需要的信息
? ? 3.? id為某模塊實例化后的ID號,在一個pipeline中此id唯一
? ? 4.? type為此模塊的類型(整個pipeline中涉及到多個類型的模塊)?
? ? 3.? parameters為該模塊需要執行時需要的特定參數,每個模塊的parameters定義可能不同
? ? 4.? ?inputs的定義,inputs中定義了一個模塊所需輸入數據的來源,framNodeId和fromOutputId代表某輸入是從哪一個前置模塊的哪一個輸出端口輸出的數據
? ? 注:某模塊的輸入端口的描述名稱(input_01,input_02)需要預先在模塊對外接口中定義好,并且一一對應,否則可能會導致傳遞的數據值或者數據類型錯亂。
三.? PipelineFramework的實現概要
? ??PipelineFramework程序框架用來加載pipeline JSON,組裝、調度模塊的順序執行,其實現有以下幾個問題需要解決:
? ?1.? 一個模塊需要執行,如何保證其依賴的所有前置模塊都已經執行結束
? ?2.? 如何在內存中將每個模塊的每個輸出端口緩存,提供給后置模塊去使用
? ?3.? 如何定義所有模塊統一的接口,然后使PipelineFramework通過統一的方式調用(類似類的多態性)
? ??第一個問題的解決:
? ? ?多次全流程模塊掃描,每一次拿出尚未執行并且已經滿足所有前置條件的模塊進行運行,執行結束后將模塊ID和執行后的結果保存到內存變量中。當某一次掃描中所有的模塊已經被執行了則退出掃描,具體代碼如下:
? ??第二個問題的解決:
? ? ?當每個模塊執行結束后的返回結果是一個map類型的數據,key為該模塊輸出端口的ID(如output_01,output_02),value為輸出數據(由于python弱類型引用,因此value可以為任意類型),部分代碼如下:
? ??第三個問題的解決:
? ? ? ? ? 1.? 每個模塊都封裝成類,且包含都兩個方法:__init__方法進行參數和輸入數據的初始化,? module_process進行具體的業務處理(如上圖)。
? ? ? ? ? 2.? 通過定義PipelineModuleFactory進行統一的模塊接口獲取和調用。
? ? ? ? ? 3.? 在PipelineFramework中進行統一的調用執行
四.? 后續的優化
? ? ?1. 通過multiprocessing 實現多模塊并行執行
? ? ? 2. 模塊運行過程監控和重試機制?