Netflix Conductor: 微服務編排器
Netflix Content Platform Engineering團隊運行著很多商務流程,這些流程由在微服務上執行的異步編排驅動。其中一些是會運行好幾天的長流程。這些流程在準備好視頻流以供全球觀眾觀看的過程中起著至關重要的作用。
這些流程包括:
- 為了內容吸收的Studio合作伙伴的集成
- 來自合作伙伴的基于 IMF 的內容吸收
- 在Netflix里搭建新字幕的流程
- 內容吸收,編碼,以及部署到CDN上
傳統上,這些流程中的一些是使用pub/sub(發布/訂閱)模式,直接調用REST以及使用數據庫管理狀態這些方法的組合來實現,以ad-hoc的方式完成整體編排。但是,隨著微服務數量的增加,以及流程復雜度的提高,如果沒有中央式的編排,理解這些分布式工作流會變得非常困難。
我們將Conductor構建為“編排引擎”,來解決如下需求,代替應用中對樣板文件的需要,同時提供交互式流程:
- 基于Blueprint。基于JSON DSL的blueprint定義執行流。
- 跟蹤并且管理工作流
- 能夠暫停,恢復以及重啟流程
- 視圖化流程流的用戶接口
- 能夠在需要時同步處理所有任務
- 能夠擴展為百萬級并發運行流程流。
- 對由客戶抽象出的隊列服務支撐
- 能夠做基于HTTP或其他傳輸協議,比如gRPC的操作。
構建Conductor是為了滿足上述需求,至今已經在Netflix使用了大概一年時間。到目前為止,它已經幫助編排了超過260萬流程,這些流程包括簡單的線性工作流,也包括非常復雜的運行數天的動態工作流。
現在,我們將 Conductor 開源,放到了社區里,希望能夠從有類似需求的其他公司學習,并且加強它的功能。
為什么不使用點對點編排?
我們發現,使用點對點任務編排很難隨著增長的業務需求和復雜度而完成擴展。Pub/sub模型適用于最簡單的流程,但是很快你就會發現該方案的一些問題,包括:
- 流程流被“嵌入到”多個應用程序的代碼里
- 通常,圍繞輸入/輸出,SLA等存在很強的耦合以及假定,這使得更加難以適應變化的需求
- 幾乎沒有辦法系統性地回答“某個電影的搭建還剩下什么任務有待完成的”?
###為什么選擇微服務?
在微服務的世界里,很多業務流程自動化是由跨服務的編排驅動的。Conductor在啟用跨服務的編排的同時,能夠提供對微服務之間交互的控制和洞察。擁有跨微服務的編排能力還幫助我們利用已有服務構建新的流,或者更新已有流讓其非常快速地就可以使用Conductor,高效地提供了引入Conductor的快捷方式。
架構概覽
該引擎的核心是狀態機服務,也稱為Decider服務。隨著工作流事件的發生(比如,任務完成,失敗等),Decider將工作流blueprint和該工作流的當前狀態組合起來,確定下一個狀態,并且調度合適的任務,并且/或者更新該工作流的狀態。
Decider和一個分布式隊列協同工作來管理調度的任務。我們在 Dynomite 之上使用 dyno-queues 來管理分布式延遲隊列。
任務Worker的實現
任務,通過worker應用程序實現,通過API層通信。Worker有兩種實現方式,要么通過可以被編排引擎調用的REST端點來實現,要么通過池循環來周期性檢查待定任務實現。Worker想要設計成冪等的無狀態功能。池模型允許我們處理worker上的反壓力,并且可以提供基于隊列深度的自動擴展能力。Conductor提供API監督每個worker的工作負載大小,可以用來自動擴展worker實例。
Worker和引擎的通信
API層
API通過HTTP暴露——使用HTTP使得可以輕松地和不同的客戶端集成。同時,添加另一種傳輸協議(比如,gRPC)應該是可能的并且相對直接。
存儲
我們使用 Dynomite “作為存儲引擎”,以及Elasticsearch索引執行流。存儲API是可插拔的,并且能夠適應多種不同的存儲系統,包括傳統的RDBMS或者Apache Cassandra這樣的no-sql存儲。
核心觀點
工作流定義
工作流定義使用基于DSL的JSON來定義。工作流blueprint定義需要執行的一系列任務。每個任務要么是一個控制任務(比如,fork(分支),join(合并),decision(決策),sub workflow(子工作流)等等),要么是一個worker任務。對工作流的定義作版本化控制,提供管理升級以及遷移的靈活性。
一個工作流定義示例:
{ "name": "workflow_name", "description": "Description of workflow", "version": 1, "tasks": [ { "name": "name_of_task", "taskReferenceName": "ref_name_unique_within_blueprint", "inputParameters": { "movieId": "${workflow.input.movieId}", "url": "${workflow.input.fileLocation}" }, "type": "SIMPLE", ... (any other task specific parameters) }, {} ... ], "outputParameters": { "encoded_url": "${encode.output.location}" } }
任務定義
每個任務的行為都受其模板的控制,該模板稱為任務定義。任務定義為每個任務提供控制參數,比如超時,重試策略等。一個任務可以是一個由應用程序實現的worker任務,也可以是由編排服務器執行的系統任務。Conductor提供了開箱即用的系統任務,比如Decision,Fork,Join,Sub Workflow,以及一個SPI,允許集成自定義的系統任務。我們也增加了對HTTP任務的支持,可以輔助調用REST服務。
任務定義的JSON片段:
{ "name": "encode_task", "retryCount": 3, "timeoutSeconds": 1200, "inputKeys": [ "sourceRequestId", "qcElementType" ], "outputKeys": [ "state", "skipped", "result" ], "timeoutPolicy": "TIME_OUT_WF", "retryLogic": "FIXED", "retryDelaySeconds": 600, "responseTimeoutSeconds": 3600 }
輸入/輸出
任務的輸入是一個map,可能是工作流初始化的一部分,或者其他任務的輸出。這樣的配置允許在工作流里路由輸入/輸出,或者允許其他任務作為輸入,這樣該任務可以在之上執行操作。比如,編碼任務的輸出可以提供給發布任務作為部署到CDN的輸入。
定義任務輸入的JSON片段:
{ "name": "name_of_task", "taskReferenceName": "ref_name_unique_within_blueprint", "inputParameters": { "movieId": "${workflow.input.movieId}", "url": "${workflow.input.fileLocation}" }, "type": "SIMPLE" }
一個例子
讓我們一起看看這個非常簡單的編碼以及部署的工作流:
這里總共涉及3個worker任務和一個控制任務(Errors):
- 內容檢查:檢查輸入路徑的文件是否正確/完整
- 編碼:生成一個視頻的編碼
- 發布:發布到CDN上
這3個任務是由不同的worker實現的,使用任務API然后放到待定任務池里。這些是理想情況下冪等的任務,對任務的輸入做操作,執行工作,并且更新狀態。
UI
UI是監控以及故障排除工作流執行情況的基本機制。UI提供對流程內部的洞察能力,允許基于不同的參數,包括輸入/輸出的搜索,并且提供了blueprint的視覺展現,以及它已經執行的路徑,來幫助大家更好地理解流程的執行情況。對于每個工作流實例來說,UI提供了每個任務執行的細節信息,包括如下細節:
- 任務被調度,被worker執行以及最終完成的時間戳。
- 如果任務失敗了,失敗的原因
- 重試次數
- 執行任務的主機
- 提供給任務的輸入,以及任務完成后的輸出。
這里是生成性能數字的kitchen sink工作流的UI片段:
考慮過的其他方案
Amazon SWF
我們使用AWS的簡單工作流做了早期版本。但是,最終選擇構建Conductor,因為SWF有一些限制:
- 需要基于blueprint的編排,而SWF要求編程式的決策器
- 視圖化工作流的UI
- 在需要時,能夠提供更多API自帶的同步特性(而不僅僅是基于消息的)
- 需要索引工作流和任務的輸入和輸出,以及能夠基于此搜索工作流
- 需要維護單獨的數據存儲來保存工作流事件,從而能夠從故障中恢復,搜索等等。
Amazon Step Function
最近發布的Amazon Step Function在編排引擎里添加了一些我們需要的特性。Conductor可能可以引入 states語言 來定義工作流。
一些統計信息
這里是我們已經運行了大概一年的生產實例的一些統計信息。大多數這些工作流都是被內容平臺使用的,用來支持內容獲取,吸收和編碼的各種工作流。
未來的考慮
- 支持AWS Lambda(或者類似)功能作為任務,用來做無服務器的簡單任務。
- 和容器編排框架緊密集成,這樣可以允許worker實例自動擴展
- 為每個任務記錄執行數據的日志。我們認為這是有助于故障診斷的很有用的功能。
- 從UI上能夠創建并且管理工作流blueprint
- 支持 states語言 。
來自:http://dockone.io/article/1930