官术网_书友最值得收藏!

1.2 Serverless規范

當對Serverless架構的基本組成及其基本工作原理/流程有了初步了解之后,為了更加深入地理解什么是Serverless,尤其是什么是FaaS,或者說什么是函數相關的問題,還需要對Serverless規范有一定的了解。CNCF對Serverless做了一定的規范和定義,例如其描述和定義了FaaS解決方案的基本模型、函數的生命周期以及觸發器類型、種類等相關規范。

本節部分內容引用自《Serverless Handbook》《CNCF Serverless Whitepaper v1.0》《Serverless Workflow Specification》《CloudEvents - Version 1.0.1》等規范文檔。

1.2.1 FaaS解決方案模型

FaaS解決方案由Event Sources、FaaS Controller、Function Instance以及平臺服務等元素組成,如圖1-6所示。

034-01

圖1-6 FaaS解決方案組成

  • Event Sources:將Event觸發或流式傳輸到一個或多個函數實例中。
  • Function Instance:可以根據需要擴展單個函數/微服務。
  • FaaS Controller:部署、控制和監視函數實例及其來源。
  • 平臺服務:FaaS解決方案使用云廠商提供的其他云服務,例如云數據庫、身份校驗等。

1.2.2 函數的規范與定義

1. 函數代碼

函數代碼、依賴項和二進制文件可以駐留在外部存儲庫中,或由用戶直接提供。如果代碼在外部存儲庫中,則用戶需要指定路徑和憑據。

Serverless框架允許用戶監聽代碼存儲庫中的更改,并在每次提交時自動構建函數鏡像/二進制文件。

函數可能依賴于外部庫或二進制文件,這些需要由用戶提供(包括描述其構建過程的方式,例如,使用Dockerfile、Zip)。

2. 函數定義

Serverless函數定義可能包含以下規范和元數據,該函數定義是特定于版本的:

  • 唯一ID;
  • 名稱;
  • 說明;
  • Labels(或tags);
  • 版本ID(或版本別名);
  • 版本創建時間;
  • 上次修改時間(函數定義);
  • 函數處理程序;
  • 運行時語言;
  • 代碼+依賴關系或代碼路徑和憑據;
  • 環境變量;
  • 執行角色;
  • 資源(所需的CPU、內存);
  • 執行超時;
  • 日志記錄失敗(私信列隊);
  • 網絡策略/VPC;
  • 數據綁定。

3. 元數據詳細信息

函數框架可能包括以下函數元數據。

  • 版本:每個函數版本應具有唯一的標識符,此外,可以使用一個或多個別名(例如latest、production、beta)來標記版本。API網關和事件源會通過版本、別名等將流量/事件路由到特定的函數版本。
  • 環境變量:用戶可以指定在運行時提供給函數的環境變量。環境變量也可以從平臺變量等派生(例如Kubernetes EnvVar定義)。環境變量使開發人員能夠控制函數行為和參數,而無須修改代碼或重建函數,從而獲得更好的開發人員體驗和函數重用。
  • 執行角色:函數應在特定的用戶或角色身份下運行,以授予和審核其對平臺資源的訪問權限。
  • 資源:定義所需或最大的硬件資源,例如函數使用的內存等。
  • 超時:指定函數調用在平臺終止之前可以運行的最長時間。
  • 故障日志(死信隊列):隊列或流的路徑,它將存儲具有適當詳細信息的失敗函數執行列表。
  • 網絡策略:分配給函數的網絡域和策略(函數與外部服務/資源進行通信)。
  • 執行語義:指定應如何執行函數(例如,每個事件至少執行一次,最多執行一次,恰好一次)。

4. 數據綁定

某些Serverless框架允許用戶指定函數使用的輸入/輸出數據資源,這使開發更簡單、性能更好(在執行期間保留數據連接、可以預取數據等)以及安全性更高(數據資源憑證是上下文的一部分,而不是代碼)。

數據綁定可以采用文件、對象、記錄、消息等形式,函數說明包括一組數據綁定定義,每個定義都指定數據資源、其憑證和使用參數。數據綁定可以引用事件數據,例如,DB鍵是從事件username字段派生的。

5. 函數輸入

函數輸入包括事件數據和元數據,還包括上下文對象。

事件詳細信息應傳遞給函數處理程序,不同的事件可能具有不同的元數據,因此需要函數能夠確定事件的類型并解析公共和特定于事件的元數據。

需要將事件類與實現分離,例如,不管流存儲是Kafka還是Kinesis,處理消息流的函數都可以運行,在這兩種情況下,它將接收消息正文和事件元數據,消息可能在不同框架之間路由。

事件可以是單個記錄(例如,在請求/響應模型中),也可以是多個記錄或微批處理(例如,在流模式中)。

FaaS解決方案使用的常見事件數據和元數據的示例如下:

  • 事件類型/種類;
  • 版本;
  • 事件;
  • 事件源;
  • 來源身份;
  • 內容類型;
  • 郵件正文;
  • 時間戳。

事件/記錄特定元數據的示例如下。

  • HTTP:Path、Method、Header、查詢參數;
  • 消息隊列:Topic、Header;
  • 記錄流:表、鍵、操作、修改時間、舊字段、新字段。

一些實現將JSON作為事件信息的數據格式并傳遞給函數。對于部分性能要求嚴格的函數(例如,流處理)或低能耗設備(IoT),這可能會增加大量的序列化/反序列化開銷。在這些情況下,可使用本地語言結構或其他序列化機制。

6. 函數上下文

調用函數時,框架提供對跨多個函數調用的平臺資源或常規屬性的訪問,而不是將所有靜態數據放入事件中或強制該函數在每次調用時初始化平臺服務。

上下文(Context)可以是一組輸入屬性、環境變量或全局變量,或是這三者的結合。

上下文示例如下:

  • 函數名稱、版本、ARN;
  • 內存限制;
  • 請求ID;
  • 地域;
  • 環境變量;
  • 安全密鑰/令牌;
  • 運行時/綁定路徑;
  • 日志;
  • 數據綁定。

有的實現初始化日志對象(例如,AWS中的全局變量或Azure中的部分上下文),用戶可以使用平臺集成的工具查看日志來跟蹤函數執行。除了傳統的日志記錄,未來的實現可能會將計數器/監控和跟蹤活動抽象為平臺上下文的一部分,以進一步提高函數的可用性。

數據綁定是函數上下文的一部分,平臺根據用戶配置啟動與外部數據資源的連接,并且這些連接可以在多個函數調用之間重用。

7. 函數輸出

當函數退出時,它可能:

  • 將值返回給調用方(例如,在HTTP請求/響應示例中);
  • 將結果傳遞到工作流程中的下一個執行階段;
  • 將輸出寫入日志。

應該有確定的方式通過返回的錯誤值或退出代碼來知道函數是成功還是失敗。

函數輸出可以是結構化的(例如HTTP響應對象),也可以是非結構化的(例如某些輸出字符串)。

1.2.3 函數生命周期

1. 函數部署流水線

如圖1-7所示,函數的生命周期從編寫代碼并提供規范元數據開始,一個Builder實體將獲取代碼和規范,然后編譯并將其轉換為工件,接下來將工件部署在具有控制器實體的集群上,該控制器實體負責基于事件流量或實例上的負載來擴展函數實例的數量。

038-01

圖1-7 函數部署流水線示意圖

2. 函數操作

Serverless框架使用以下動作控制函數的生命周期。

  • 創建:創建新函數,包括其規范和代碼。
  • 發布:創建可以在集群上部署的函數的新版本。
  • 更新別名/標簽(版本):更新版本別名。
  • 執行/調用:不通過事件源調用特定版本。
  • 事件源關聯:將函數的特定版本與事件源連接。
  • 獲取:返回函數元數據和規范。
  • 更新:修改函數的最新版本。
  • 刪除:刪除函數,可以刪除特定版本或所有版本的函數。
  • 列表:顯示函數及其元數據的列表。
  • 獲取統計信息:返回有關函數運行時使用情況的統計信息。
  • 獲取日志:返回函數生成的日志。

上述操作在實際過程中的流程示意圖如圖1-8所示。

038-02

圖1-8 函數創建/更新流程示意圖

1)在創建函數時,提供其元數據作為函數創建的一部分,將對其進行編譯使其具有可發布的特性,接下來可以啟動、禁用函數。函數部署需要能夠支持以下用例。

①事件流:在此用例中,隊列中可能始終存在事件,但是可能需要通過請求暫停/恢復進行處理;

②熱啟動:在任何時候具有最少實例數的函數,使得所接收的“第一”事件具有熱啟動,因為該函數已經部署并準備好為事件服務(而不是冷啟動),其中函數獲得通過“傳入”事件在第一次調用時部署。

2)用戶可以發布一個函數,這將創建一個新版本(最新版本的副本),發布的版本可能會被標記或有別名。

3)用戶可能希望直接執行/調用函數(繞過事件源或API網關)以進行調試和開發過程。用戶可以指定調用參數,例如所需版本、同步/異步操作、詳細日志級別等。

4)用戶可能想要獲得函數統計(例如調用次數、平均運行時間、平均延遲、失敗、重試等)。

5)用戶可能想要檢索日志數據。這可以通過嚴重性級別、時間范圍、內容來過濾。Log數據是每個函數級別的,它包括諸如函數創建和刪除、警告或調試消息之類的事件,以及可選的函數的Stdout或Stderr。優選每次調用具有一個日志條目或者將日志條目與特定調用相關聯的方式(以允許更簡單地跟蹤函數執行流)。

3. 函數版本控制和別名

一個函數可能具有多個版本,使用戶能夠運行不同級別的代碼,例如beta/production、A/B測試等。使用版本控制時,默認情況下函數版本為latest,latest版本可以進行更新和修改,可能會在每次更改時觸發新的構建過程。

如果用戶想要凍結一個版本可以使用發布操作,該操作將創建一個具有潛在標簽或別名(例如beta、production)的新版本以配置事件源,事件或API調用可以被路由到特定的函數版本。非最新的函數版本是不可變的(它們的代碼以及所有或某些函數規范),并且一旦發布就不能更改。函數不能“未發布”,而應將其刪除。另外,當前的大多數實現都不允許函數branch/fork(更新舊版本代碼),因為這會使實現和用法變得復雜,但是將來可能需要這樣做。

當同一函數有多個版本時,用戶必須指定要操作的函數版本以及如何在不同版本之間劃分事件流量。例如,用戶可以決定路由90%的事件流量到穩定版本,10%的流量到Beta版(又稱canary update)。可以通過指定確切版本或通過指定版本別名來實現,版本別名通常將引用特定的函數版本。

用戶創建或更新函數時,它可能會根據變更的性質來驅動新的構建和部署。

4. 事件源到函數關聯

由于事件源觸發事件而調用函數。函數和事件源之間存在一個n:m映射。每個事件源都可以用于調用多個函數,而一個函數可以由多個事件源觸發。事件源可以映射到函數的特定版本或函數的別名,后者提供了一種用于更改函數并部署新版本的方法,而無需更改事件關聯。事件源還可以定義為使用同一函數的不同版本,并定義應為每個函數分配多少流量。

創建函數后或稍后的某個時間,需要關聯事件源,該事件源應觸發作為該事件的函數調用。這需要一系列動作和方法,例如:

  • 創建事件源關聯;
  • 更新事件源關聯;
  • 列出事件源關聯。

5. 事件源

不同類型的事件源如下所示。

  • 事件和消息傳遞服務,例如RabbitMQ、MQTT、SES、SNS、Google Pub/Sub。
  • 存儲服務,例如S3、DynamoDB、Kinesis、Cognito、Google Cloud Storage,Azure Blob、iguazio V3IO(對象/流/數據庫)。
  • 端點服務,例如物聯網、HTTP網關、移動設備、Alexa、Google Cloud Endpoint。
  • 配置存儲庫,例如Git、CodeCommit。
  • 使用特定于語言的SDK的用戶應用程序。
  • SchEnable定期調用函數。

盡管每個事件提供的數據在不同事件源之間可能會有所不同,但事件結構應該具有通用性,能夠封裝有關事件源的特定信息(詳細信息見事件數據和元數據)。

6. 函數要求

根據當前的技術水平,函數和Serverless運行時應滿足的一組通用要求如下。

  • 函數必須與不同事件類的基礎實現分離。
  • 可以從多個事件源調用函數。
  • 無須為每個調用方法使用不同的函數。
  • 事件源可以調用多個函數。
  • 函數可能需要一種與基礎平臺服務進行持久綁定的機制,可能是跨函數調用。函數的壽命可能很短,但是如果需要在每次調用時都進行引導,那么引導可能會很昂貴,例如在日志記錄、連接、安裝外部數據源的情況下。
  • 同一個應用程序中每個函數可以使用不同的語言編寫。
  • 函數運行時應盡可能減少事件序列化和反序列化的開銷(例如,使用本地語言結構或有效的編碼方案)。

7. 工作流相關要求

工作流相關要求如下:

  • 函數可以作為工作流的一部分被調用,一個函數的結果可以作為另一個函數的觸發;
  • 可以由事件或“and/or事件組合”觸發函數;
  • 一個事件可能觸發按順序或并行執行的多個函數;
  • “and/or事件組合”可能觸發順序運行、并行運行或分支運行的m個函數;
  • 在工作流的中間,可能會收到不同的事件或函數結果,這將觸發分支切換到不同的函數;
  • 函數的部分或全部結果需要作為輸入傳遞給另一個函數;
  • 函數可能需要一種與基礎平臺服務進行持久綁定的機制,這可能是跨函數調用或函數壽命很短。

8. 函數調用類型

函數調用類型如圖1-9所示,可以根據不同的用例從不同的事件源調用函數,例如:

  • 同步請求(Req/Rep),例如HTTP請求、gRPC調用。
  • 客戶發出請求并等待立即響應。
  • 異步消息隊列請求(發布/訂閱),例如RabbitMQ、AWS SNS、MQTT、電子郵件、對象(S3)更改、計劃事件(如CRON作業)。
  • 消息發布到交換機并分發給訂閱者;
  • 沒有嚴格的消息排序,以單次處理為粒度。
  • 消息/記錄流:例如Kafka、AWS Kinesis、AWS DynamoDB Streams。
  • 通常,每個分片使用單個工作程序(分片消費者)將流分片為多個分區/分片;
  • 可以從消息、數據庫更新(日志)或文件(例如CSV、JSON、Parquet)生成流;
  • 事件可以推送到函數運行時或由函數運行時拉動。
  • 批量作業,例如ETL作業、分布式機器學習、HPC模擬。
  • 作業被調度或提交到隊列,并在運行時中使用并行的多個函數實例進行處理,每個函數實例處理工作集的一個或多個部分(任務);
  • 當所有并行工作程序完成所有計算任務時,作業完成。
041-01

圖1-9 函數調用類型

1.2.4 其他規范

眾所周知,Serverless應用是由事件驅動的,當應用觀察的事件源中有情況發生時,就會觸發相應的函數。函數執行后會到達某個狀態,就像狀態機一樣,隨著一系列的事件發生,會觸發函數順序,并會并行運行,這里就會涉及事件數據結構、傳遞與工作流的規范。

1. CloudEvent

Serverless應用是由事件驅動的,事件產生者傾向于以不同的方式描述事件,這就導致Serverless應用在同一云廠商的不同類型的事件中是不同的,或者同一種事件在不同云廠商中的表現是不同的。缺少通用的事件描述方式意味著開發人員需要不斷重新學習如何使用事件。這也限制了庫、工具和基礎架構幫助跨環境(例如SDK、事件路由器或跟蹤系統)傳遞事件數據的潛力,讓事件數據實現的可移植性和生產率受影響。

CloudEvent是以通用格式描述事件數據的規范,以提供跨服務、平臺和系統的互操作性。事件格式指定如何使用某些編碼格式序列化CloudEvent。支持這些編碼兼容CloudEvent的實現必須遵守相應事件格式中指定的編碼規則。所有實現都必須支持JSON格式。

圖1-10所示是對CloudEvent中部分術語的關系描述。

042-01

圖1-10 CloudEvent基礎流程與名字關系簡圖

  • Occurrence(發生):是在軟件系統運行期間捕獲的事實陳述。這可能是由于系統發出的信號或系統正在觀察的信號因狀態變化、計時器完成或任何其他值得注意的活動而發生的。例如,由于電池電量不足或虛擬機將要執行重啟計劃,設備可能會進入警報狀態。
  • Event(事件):表示Occurrence及其上下文的數據記錄。Event從Event生產者(Source)路由到感興趣的Event使用者。Event路由可以基于Event中包含的信息,但是Event不會標識特定的路由目的地。Event包含兩種類型的信息:表示Occurrence的Event Data和提供有關Occurrence上下文信息的Context元數據。一次發生可能會導致多個事件。
  • Producer(生產者):是創建描述CloudEvent的數據結構的特定實例、過程或設備。
  • Source(源):是發生事件的上下文。在分布式系統中,Source可能包含多個Producer。如果Source不知道CloudEvent,則外部Producer將代表Source創建CloudEvent。
  • Consumer(消費者):接收事件并對其采取行動。Consumer使用上下文和數據執行某些邏輯,這可能導致新Event的發生。
  • Intermediary(中介):接收包含Event的消息,目的是將Event轉發到下一個接收者,該接收者可能是另一個Intermediary或Consumer。Intermediary的典型任務是根據Context中的信息將Event路由到接收者。
  • Context(上下文):元數據封裝在Context Attribute(上下文屬性)中。工具和應用程序代碼可以使用此信息來標識Event與系統各方面或其他Event的關系。
  • Data(數據):有關事件的特定于域的信息(即有效負載)。這可能包括有關Occurrence的信息,有關已更改數據的詳細信息或更多信息。
  • Message(消息):Event是通過消息從源傳遞到目的地。
  • Protocol(協議):可以通過各種行業標準協議(例如HTTP、AMQP、MQTT、SMTP)、開源協議(如Kafka、NATS)或特定于平臺/供應商的協議(AWS Kinesis、Azure Event Grid)傳遞Message。

(1)Context Attribute(上下文屬性)

每個符合此規范的CloudEvent必須包含指定為REQUIRED的上下文屬性,并且可以包括一個或多個OPTIONAL上下文屬性。

這些屬性雖然描述了事件,但設計為可以獨立于事件數據進行序列化。這樣就可以在目的地對它們進行檢查,而不必對事件數據進行反序列化。

(2)屬性命名規范

CloudEvent規范定義了到各種協議和編碼的映射,隨附的CloudEvent SDK針對各種運行時和語言。其中一些將元數據元素視為區分大小寫,而其他元素則不區分大小寫,并且單個CloudEvent可能會通過涉及協議、編碼和運行時混合的多個躍點進行路由。因此,本規范限制了所有屬性的可用字符集,以防止區分大小寫問題或與通用語言中標識符的允許字符集相沖突。CloudEvent屬性名稱必須由ASCII字符集的小寫字母(a~z)或數字(0~9)組成,并且必須以小寫字母開頭。屬性名稱應具有描述性和簡潔性,長度不得超過20個字符。

(3)類型系統

以下抽象數據類型可用于屬性,每個類型都可以以不同的方式表示,包括“通過不同的事件格式”和“在傳輸元數據字段中”。該規范為所有實現必須支持的每種類型定義了規范的字符串編碼。

1)Required屬性。以下屬性必須出現在所有CloudEvent中。

  • id:String
  • source:URI-reference
  • specversion:String
  • type:String

2)OPTIONAL屬性。以下屬性是可選的,可以出現在CloudEvent中。

  • datacontenttype:String
  • dataschema:URI
  • subject:String
  • time:Timestamp

3)擴展Context Attribute。CloudEvent Producer可以在Event中包含其他Context Attribute,這些屬性可以在與Event處理相關的輔助操作中使用。

該規范對擴展屬性的語義沒有任何限制,但必須使用類型系統中定義的類型。擴展的每個定義都應完全定義屬性的所有方面,例如屬性的名稱、語義含義和可能的值,甚至表明它對其值沒有任何限制。新的擴展名定義應該使用具有足夠描述性的名稱,以減少與其他擴展名發生名稱沖突的幾率。特別是擴展作者應該檢查擴展文檔中的已知擴展集,不僅是可能的名稱沖突,還是可能感興趣的擴展。

每個定義如何序列化CloudEvent的規范都將定義擴展屬性的顯示方式。

擴展屬性必須使用與所有CloudEvent上下文屬性相同的常規模式進行序列化。例如,在二進制HTTP中,這意味著它們必須顯示為帶有ce-前綴的HTTP標頭。屬性的規范可以定義一個二級序列化,其中數據在消息中的其他位置重復。

在定義了二級序列化的情況下,擴展規范還必須說明如果兩個序列化位置的數據不同,CloudEvent的接收者將要做什么。另外,發送者需要為intermediary和接收者不知道其擴展的情況做好準備,因此專用序列化版本很可能不會作為CloudEvent擴展屬性進行處理。

許多傳輸支持發送者包括附加元數據的功能,例如作為HTTP標頭。雖然未強制要求CloudEvent接收器處理和傳遞它們,但建議通過某種機制來進行處理,以使其清楚地知道它們不是CloudEvents的元數據。

這是一個說明需要其他屬性的示例。在許多物聯網和企業用例中,Event可以在無服務器應用程序中使用,該應用程序跨多種Event類型執行操作。為了支持這種用例,Event Producer需要向Context Attribute添加其他標識屬性,Event使用者可以使用這些屬性將這個事件與其他事件相關聯。如果此類身份屬性恰好是事件Data的一部分,則Event生成器還將身份屬性添加到Context Attribute,Event使用者可以輕松訪問此信息,而無須解碼和檢查Event Data。此類身份屬性還可用于幫助中間網關確定如何路由Event。

4)Event Data(事件數據)。按照術語Data(數據)的定義,CloudEvent可以包含有關事件的特定于域的信息。如果存在,此信息將封裝在數據中。

(4)Size Limit(大小限制)

在許多情況下,CloudEvent將通過一個或多個通用Intermediary進行轉發,每個inter-mediary都可能會對轉發Event的大小施加限制。CloudEvent也可能會路由到受存儲或內存限制的Consumer(如嵌入式設備),因此會遇到大型單一事件。

Event的“Size(大小)”是其線路大小,根據所選的Event格式和所選的協議綁定,傳輸frame-metadata(幀元數據)、event metadata(事件元數據)和event data(事件數據)。

如果應用程序配置要求Event跨不同的傳輸進行路由或Event進行重新編碼,則應該考慮應用程序使用的效率最低的傳輸和編碼應符合以下大小限制:

  • Intermediary必須轉發大小為64 KB或更小的事件。
  • Consumer應該接受至少64 KB的事件。

實際上,這些規則將允許Producer安全地發布最大為64KB的Event。此處的安全是指通常合理的做法是,期望所有Intermediary都接受并轉發該事件。無論是出于本地考慮,還是要接受或拒絕該大小的事件,它都在任何特定的Consumer控制之下。

通常,CloudEvent發布者應該通過避免將大型數據項嵌入Event有效負載中來保持事件緊湊,而是將Event有效負載鏈接到此類數據項。從訪問控制的角度來看,此方法還允許Event的更廣泛分布,因為通過解析鏈接訪問Event相關的詳細信息可實現差異化的訪問控制和選擇性公開,而不是將敏感的詳細信息直接嵌入事件中。

(5)隱私與安全

互操作性是此規范的主要推動力,要使這種行為成為可能,就需要明確提供一些信息,從而可能導致信息泄漏。

需要考慮以下事項,以防止意外泄漏,尤其是在利用第三方平臺和通信網絡時:

  • Context Attribute:敏感信息不應攜帶和表示在上下文屬性中。CloudEvent Producer、Consumer和Intermediary可以內省并記錄Context Attribute。
  • 數據:特定于域的Event數據應該被加密以限制對可信方的可見性。用于這種加密的機制是Producer和Consumer之間的協議,因此不在本規范的范圍之內。
  • 傳輸綁定:應當采用傳輸級安全性來確保CloudEvent的可信和安全交換。

2. Workflow

Workflow是供應商中立的規范,用于定義用戶指定或描述其Serverless應用程序流的格式或原語。

許多Serverless應用程序不是由單個事件觸發的簡單函數,而是由系列函數執行的多個步驟組成,而函數在不同步驟中由不同事件觸發。如果某個步驟涉及多個函數,則該步驟中的函數可能會根據不同的事件觸發器依次執行、并行執行或在分支中執行。為了使Serverless平臺正確執行Serverless應用程序的函數工作流程,應用程序開發人員需要提供工作流程規范。

為了給業界提供一種標準方法,CNCF Serverless工作組成立了Workflow子組,供用戶指定其Serverless應用程序工作流,以促進Serverless應用程序在不同供應商平臺之間的可移植性。

為此CNCF Serverless工作組Workflow小組制定了一個完整的協議,使給定的事件時間軸和工作流始終產生相同的作用。

(1)功能范圍

函數工作流用于將函數編排為協調的微服務應用程序。函數工作流中的每個函數可能由來自各種來源的事件驅動。函數工作流將函數和觸發事件分組到一個連貫的單元中,并描述函數的執行和以規定方式傳遞的信息。具體來說,函數工作流程允許用戶:

  • 定義Serverless應用程序中涉及的步驟/狀態和工作流程。
  • 定義每個步驟中涉及的函數。
  • 定義哪個事件或事件組合觸發一個或多個函數。
  • 定義在觸發多個函數時如何安排這些函數依次執行還是并行執行。
  • 指定如何在函數或狀態之間過濾信息和傳遞事件。
  • 定義在哪種錯誤狀態下需要重試。
  • 如果函數是由兩個或多個事件觸發的,則定義應使用什么標簽/鍵將那些事件與相同的函數工作流實例相關聯。

如圖1-11所示是涉及事件和函數的函數工作流的示例。使用這樣的函數工作流,用戶可以輕松指定事件與函數之間的交互以及如何在工作流程中傳遞信息。

046-01

圖1-11 事件和函數的函數工作流示例圖

使用函數工作流,用戶可以定義集合點(狀態)以等待預定義的事件,然后再執行一個或多個函數并繼續執行函數工作流。

(2)Workflow模型

可以將函數工作流(Function Workflow)視為狀態的集合以及這些狀態之間的轉換和分支,并且每個狀態可以具有關聯的事件和/或功能。函數工作流可以從CLI命令調用,也可以在事件從事件源到達時動態觸發。來自事件源的事件也可能與函數工作流中的特定狀態相關聯。函數工作流中的這些狀態將等待一個或多個事件源中的一個或多個事件到達,然后再執行其關聯的操作并進入下一個狀態。其他工作流程功能包括:

  • 函數的結果可用于啟動重試操作或確定下一個要執行的函數或要轉換到的狀態。
  • 函數工作流提供了一種在事件處理過程中對JSON事件有效負載進行過濾和轉換的方法。
  • 函數工作流為應用程序開發人員提供了一種在事件中指定唯一字段的方法,該字段可用于將事件源中的事件關聯到同一函數工作流實例。

可以很自然地將函數工作流建模為狀態機。以下是函數工作流的定義/規范提供的狀態列表。工作流的規范稱為工作流程模板。工作流模板的實例稱為工作流實例(Workflow Instance)。

  • Event State(事件狀態):用于等待事件源中的事件,然后調用一個或多個函數以順序或并行運行。
  • Operation State(操作狀態):允許一個或多個函數按順序或并行運行,而無須等待任何事件。
  • Switch State(切換狀態):允許轉換到其他多個狀態(例如,不同的函數導致前一個狀態觸發分支/轉換到不同的下一個狀態)。
  • Delay State(延遲狀態):使工作流執行延遲指定的持續時間或直到指定的時間/日期。
  • End State(結束狀態):失敗/成功終止工作流。
  • Parallel State(并行狀態):允許多個狀態并行執行。

函數工作流由工作流規范描述。

(3)工作流規范

函數工作流規范定義了函數工作流的行為和操作。函數工作流規范結構應允許用戶定義事件到達觸發的執行函數。它應具有足夠的靈活性,以涵蓋從單個函數的簡單調用到涉及多個函數和多個事件的復雜應用程序和各種微服務應用程序。

從高層看,函數工作流規范包括兩部分:觸發器定義(trigger definition)和狀態定義(state definition)。

{
    "trigger-defs" : [],
    "states": []
}

trigger-defs數組(僅在存在與工作流關聯的事件時才需要)是與函數工作流關聯的事件觸發器的數組。如果應用程序工作流中涉及多個事件,則必須在該事件觸發器中指定一個用于將事件與同一工作流實例的其他事件相關聯的關聯令牌(correlation-token)。狀態數組(必需)是與函數工作流相關聯的狀態數組。下面是JSON格式的函數工作流示例,其中涉及事件狀態和該事件狀態的觸發器:

{
    "trigger-defs":[
        {
            "name":"OBS-EVENT",
            "source":"CloudEvent source",
            "eventID":"CloudEvent eventID",
            "correlation-token":"A path string to an identification label field in the event message"
        },
        {
            "name":"TIMER-EVENT",
            "source":"CloudEvent source",
            "eventID":"CloudEvent eventID",
            "correlation-token":"A path string to an identification label field in the event message"
        }
    ],
    "states":[
        {
            "name":"STATE-OBS",
            "start":true,
            "type":"EVENT",
            "events":[
                {
                    "event-expression":"boolean expression 1 of triggering events",
                    "action-mode":"Sequential or Parallel",
                    "actions":[
                        {
                            "function":"function name 1"
                },
                {
                    "function":"function name 2"
                }
            ],
            "next-state":"STATE-END"
            },
            {
                "event-expression":"boolean expression 2 of triggering events",
                "action-mode":"Sequential or Parallel",
                "actions":[
                    {
                        "function":"function name 3"
                    },
                    {
                        "function":"function name 4"
                    }
                ],
                "next-state":"STATE-END"
            }
            ]
        },
        {
            "name":"SATATE-END",
            "type":"END"
        }
    ]
}

以下是帶有操作狀態的函數工作流的另一個示例:

{
    "states":[
        {
            "name":"STATE-ALARM-NOTIFY",
            "start":true,
            "type":"OPERATION",
            "action-mode":"Sequential or Parallel",
            "actions":[
                {
                    "function":"function name 1"
                },
                {
                    "function":"function name 2"
                }
            ],
            "next-state":"STATE-END"
        }
    ]
}

(4)觸發器定義

trigger-defs數組由一個或多個事件觸發器組成。以JSON格式定義的事件觸發器示例如下:

{
    "trigger-defs":[
        {
            "name":"EVENT-NAME",
            "source":"CloudEvent source",
            "eventID":"CloudEvent eventID",
            "correlation-token":"A path string to an identification label field in the event message"
        }
    ]
}

(5)動作定義{#action-definition}

下面是JSON格式的定義。

{
    "actions":[
        {
            "function":"FUNCTION-NAME",
            "timeout":"TIMEOUT-VALUE",
            "retry":[
                {
                    "match":"RESULT-VALUE",
                    "retry-interval":"INTERVAL-VALUE",
                    "max-retry":"MAX-RETRY",
                    "next-state":"STATE-NAME"
                }
            ]
        }
    ]
}
  • function:指定調用的函數。
  • timeout:從請求發送給函數開始計時,等待函數指定完成的時間,單位為秒,必須為正整數。
  • retry:重試策略。
  • match:匹配的結果值。
  • retry-intervalmax-retry:當出現錯誤時使用。
  • next-state:當超過max-retry限制后到轉移到下一個狀態。

(6)狀態定義

1)事件狀態(Event State)。

{
    "states":[
        {
            "name":"STATE-NAME",
            "type":"EVENT",
            "start":true,
            "events":[
                {
                    "event-expression":"EVENTS-EXPRESSION",
                    "timeout":"TIMEOUT-VALUE",
                    "action-mode":"ACTION-MODE",
                    "actions":[
                    ],
                    "next-state":"STATE-NAME"
                }
            ]
        }
    ]
}

事件狀態必須將type值指定為EVENT。

  • start:是否為起始狀態。可選的字段,默認為false。
  • events:與該事件狀態相關的事件數組。
  • event-expression:這是一個布爾表達式,由一個或多個事件操作數和布爾運算符組成。EVENTS-EXPRESSION可以是Event1 or Event2。到達并匹配EVENTS-EXPRESSION的第一個事件將導致執行此狀態的所有操作,然后轉換到下一個狀態。
  • timeout:指定在EVENTS-EXPRESSION中等待事件的時間段。如果事件不在超時時間內發生,則工作流將轉換為結束狀態。
  • action-mode:指定函數是順序執行還是并行執行,并且可以是SEQUENTIAL或PARALLEL。
  • next-state:指定在成功執行所有匹配事件的操作之后要轉換到的下一個狀態的名稱。

2)操作狀態(Operation State)。

{
    "states":[
        {
            "name":"STATE-NAME",
            "type":"OPERATION",
            "start":true,
            "action-mode":"ACTION-MODE",
            "actions":[
            ],
            "next-state":"STATE-NAME"
        }
    ]
}
  • action-mode:指定函數是順序執行還是并行執行,并且可以是SEQUENTIAL或PARALLEL。
  • actions:由一系列動作構成的列表,指定接收到與事件表達式匹配的事件時要執行的函數的列表。
  • next-state:指定在成功執行所有匹配事件的操作之后要轉換到的下一個狀態的名稱。

3)分支狀態(Switch State)。

{
    "states":[
        {
            "name":"STATE-NAME",
            "type":"SWITCH",
            "start":true,
            "choices":[
                {
                    "path":"PAYLOAD-PATH",
                    "value":"VALUE",
                    "operator":"COMPARISON-OPERATOR",
                    "next-state":"STATE-NAME"
                },
                {
                    "Not":{
                        "path":"PAYLOAD-PATH",
                        "value":"VALUE",
                        "operator":"COMPARISON-OPERATOR"
                    },
                    "next-state":"STATE-NAME"
                },
                {
                    "And":[
                        {
                            "path":"PAYLOAD-PATH",
                            "value":"VALUE",
                            "operator":"COMPARISON-OPERATOR"
                        },
                        {
                            "path":"PAYLOAD-PATH",
                            "value":"VALUE",
                            "operator":"COMPARISON-OPERATOR"
                        }
                    ],
                    "next-state":"STATE-NAME"
                },
                {
                    "Or":[
                        {
                            "path":"PAYLOAD-PATH",
                            "value":"VALUE",
                            "operator":"COMPARISON-OPERATOR"
                        },
                        {
                            "path":"PAYLOAD-PATH",
                            "value":"VALUE",
                            "operator":"COMPARISON-OPERATOR"
                        }
                    ],
                    "next-state":"STATE-NAME"
                }
            ],
            "default":"STATE-NAME"
        }
    ]
}
  • choices:針對輸入數據定義了一個有序的匹配規則集,以使數據進入此狀態,并為每個匹配項轉換為下一個狀態。
  • path:JSON Path,用于選擇要匹配的輸入數據的值。
  • value:匹配值。
  • operator:指定如何將輸入數據與值進行比較,例如“EQ”“LT”“LTEQ”“GT”“GTEQ”“StrEQ”“StrLT”“StrLTEQ”“StrGT”“StrGTEQ”。
  • next-state:指定在存在值匹配時要轉換到的下一個狀態的名稱。
  • Not:必須是單個匹配規則,且不得包含next-state字段。
  • And和Or:必須是匹配規則的非空數組,它們本身不能包含next-state字段。
  • default:如果任何選擇值都不匹配,則default字段將指定下一個狀態的名稱。
  • next-state:評估的順序是從上到下,如果發生匹配,請轉到next-state,并忽略其余條件。

4)延遲狀態(Delay State)。

{
    "states":[
        {
            "name":"STATE-NAME",
            "type":"DELAY",
            "start":true,
            "time-delay":"TIME-VALUE",
            "next-state":"STATE-NAME"
        }
    ]
}
  • time-delay:指定時間延遲。TIME-VALUE是在此狀態下延遲的時間(以秒為單位),必須是正整數。
  • next-state:指定要轉換到的下一個狀態的名稱。STATE-NAME在函數工作流中必須是有效的State名稱。

5)結束狀態(End State)。

{
    "states": [
        {
            "name": "STATE-NAME",
            "type": "END",
            "status": "STATUS"
        }
    ]
}
  • status:該字段必須為SUCCESS或FAILURE,表示工作流結束。

6)并行狀態(Parallel State)。

并行狀態由多個并行執行的狀態組成。并行狀態具有多個同時執行的分支。每個分支都有一個狀態列表,其中一個狀態為開始狀態。每個分支繼續執行,直到達到該分支內沒有下一個狀態的狀態為止。當所有分支都執行完成后,并行狀態將轉換為下一個狀態。本質上,這是在并行狀態內嵌套一組狀態。

并行狀態由狀態類型PARALLEL定義,并包括一組并行分支,每個分支都有自己的獨立狀態。每個分支都接收并行狀態的輸入數據的副本。除END狀態外,任何類型的狀態都可以在分支中使用。

分支內狀態的next-state轉換只能是到該分支內的其他狀態。另外,并行狀態之外的狀態不能轉換到并行狀態的分支內的狀態。

并行狀態會生成一個輸出數組,其中每個元素都是分支的輸出。輸出數組的元素不必是同一類型。

{
    "states":[
        {
            "name":"STATE-NAME",
            "type":"PARALLEL",
            "start": true,
            "branches":[
                {
                    "name":"BRANCH-NAME1",
                    "states":[
                    ]
                },
                {
                    "name":"BRANCH-NAME2",
                    "states":[
                    ]
                }
            ],
            "next-state":"STATE-NAME"
        }
    ]
}
  • branch:同時執行的分支的列表。每個命名分支都有一個states列表。分支內每個狀態的next-state字段必須是該分支內的有效狀態名稱,或者不存在以指示該狀態終止該分支的執行。分支執行從分支內具有"start": true的狀態開始。
  • next-state:指定在所有分支完成執行之后要轉換到的下一個狀態的名稱。STATE-NAME在函數工作流中,必須是有效的狀態名,但不能是已處于并行狀態中的狀態。

(7)信息傳遞

如圖1-12顯示了函數工作流的數據流,該函數工作流包括調用兩個函數的事件狀態。來自一個狀態的輸出數據作為輸入數據傳遞到下一狀態。過濾器用于過濾和轉換進入和退出每個狀態時的數據。從工作流中的Operation State調用時,來自先前狀態的輸入數據可能會傳遞到Serverless函數。

055-01

圖1-12 函數工作流的數據流1

來自Serverless函數的響應中包含的數據將作為輸出數據發送到下一個狀態。如果狀態(Operation State或Event State)包括一系列順序操作,則將過濾來自一個Serverless函數的響應中包含的數據,然后在請求中將其發送給下一個函數。

在Event State下,在將請求從事件源接收到的CloudEvent元數據傳遞到Serverless函數之前,可以對其進行轉換并將其與從先前狀態接收到的數據進行組合。

同樣,在從Serverless函數的響應中接收到的CloudEvent元數據可以轉換并與從先前狀態接收到的數據組合,然后再在發送到事件源的響應中進行傳遞。

在某些情況下,諸如API網關之類的事件源希望收到工作流的響應。在這種情況下,可以將從Serverless函數的響應中接收到的CloudEvent元數據進行轉換,并與從先前狀態接收到的數據進行組合,然后再將其在發送到事件源的響應中進行傳遞,如圖1-13所示。

056-01

圖1-13 函數工作流的數據流2

(8)過濾器機制

狀態機維護一個隱式JSON數據,該數據可以從每個過濾器作為JSONPath表達式$進行訪問。過濾器共有三種。

  • 事件過濾器(Event Filter)。
  • 當數據從事件傳遞到當前狀態時調用。
  • 狀態過濾器(State Filter)。
  • 當數據從先前狀態傳遞到當前狀態時調用;
  • 當數據從當前狀態傳遞到下一個狀態時調用。
  • 動作過濾器(Action是指定義Serverless函數的動作定義)。
  • 當數據從當前狀態傳遞到第一個操作時調用;
  • 當數據從一個動作傳遞到另一個動作時調用;
  • 當數據從最后一個動作傳遞到當前狀態時調用。

每個過濾器都有三種路徑過濾器:

  • InputPath。
  • 選擇事件、狀態或操作的輸入數據作為JSONPath默認值為$。
  • ResultPath。
  • 將Action輸出的結果JSON節點指定為JSONPath;
  • 默認值為$。
  • OutputPath。
  • 將State或Action的輸出數據指定為JSONPath;
  • 默認值為$。

(9)錯誤

狀態機在運行時返回以下預定義的錯誤代碼。通常,它在動作定義的retry字段中使用。

  • SYS.Timeout;
  • SYS.Fail;
  • SYS.MatchAny;
  • SYS.Permission;
  • SYS.InvalidParameter;
  • SYS.FilterError。
主站蜘蛛池模板: 剑阁县| 南木林县| 申扎县| 察隅县| 沐川县| 明溪县| 蒲城县| 大城县| 白城市| 通州市| 长泰县| 兖州市| 武功县| 城步| 全椒县| 太湖县| 阿城市| 闸北区| 社会| 铁岭市| 威远县| 三明市| 巴青县| 高邮市| 黎城县| 江门市| 滦南县| 石首市| 扎赉特旗| 青海省| 大港区| 大荔县| 杭锦旗| 贡嘎县| 奉化市| 温宿县| 清河县| 屏南县| 册亨县| 阿坝| 淄博市|