Durable Functions (Azure Functions) 中的效能和級別
若要最佳化效能和延展性,務必了解 Durable Functions 獨特的調整規模特性。 本文中,將說明如何根據負載將背景工作角色縮放比例,以及如何調整各種參數。
背景工作角色縮放比例
工作中樞概念的基本優點為可以持續調整處理工作中樞工作項目之背景工作角色數目。 具體來說,若工作需要更快速地處理,應用程式可以新增更多背景工作角色 (擴增),此外,若沒有足夠工作讓背景工作角色保持忙碌,則可以移除 (縮減) 背景工作角色。 若工作中樞完全閒置,甚至可以將其縮放至零。 在縮放至零時,完全無任何背景工作角色;只有縮放控制器和儲存體需要保持作用中狀態。
下圖說明此概念:
自動縮放
如同在取用和彈性進階方案中執行的所有 Azure Functions 一樣,Durable Functions 也透過 Azure Functions 縮放控制器來支援自動縮放。 縮放控制器會監視訊息和工作在處理之前必須等候的時間長短。 根據這些延遲,它可以決定是否要新增或移除背景工作角色。
注意
從 Durable Functions 2.0 開始,函數應用程式可以設定為在彈性進階方案中受 VNET 保護的服務端點內執行。 在此設定中,Durable Functions 觸發程序會起始縮放要求,而不是縮放控制器。 如需詳細資訊,請參閱執行階段縮放監視。
在進階方案上,自動縮放有助於保持背景工作角色數目 (以及因此產生的作業成本) 與應用程式正在處理的負載大致成比例。
CPU 使用量
協調器函式會在單一執行緒上執行,以確保執行作業可透過多次重新執行來做出確定。 由於此單一執行緒執行作業的緣故,所以協調器函式執行緒切勿執行需要大量 CPU 的工作、進行 I/O 或因故封鎖。 任何可能需要 I/O、封鎖或多個執行緒的工作,都應該移入活動函式中。
活動函式和一般佇列觸發的函式具備完全相同之行為。 它們可以安全地執行 I/O、執行需要大量 CPU 的作業,以及使用多個執行緒。 因為活動觸發程序是無狀態,所以能夠自由地向外延展至不限數量的虛擬機器。
實體函式亦在單一執行緒上執行,而且一次只會處理一項作業。 但實體函式完全不限制可執行的程式碼類型。
函式逾時
如同所有 Azure Functions 一樣,活動、協調器和實體函式也受限於相同的函式逾時。 一般而言,Durable Functions 同樣將函式逾時視為應用程式程式碼擲回的未處理例外狀況。
例如,假設活動逾時,則會將函式執行記錄為失敗,並通知協調器來處理逾時,就像其他任何例外狀況一樣:重試 (如果呼叫有指定),或執行例外狀況處理常式。
實體作業批次
若要改善效能並降低成本,單一工作項目可能會執行整個批次的實體作業。 在使用量方案上,每個批次都會以單一函式執行來計費。
批次大小上限預設為 50 (適合使用量方案) 和 5000 (適合其他所有方案)。 在 host.json 檔案中也可以設定批次大小上限。 如果批次大小上限為 1,實際上是停用批次。
注意
如果個別實體作業執行很久,則最好限制批次大小上限以降低函式逾時的風險,特別是取用方案。
執行個體快取
一般而言,若要進行處理協調流程工作項目,背景工作角色必須同時處理兩者:
- 擷取協調流程的歷程。
- 使用歷程來重新執行協調器程式碼。
若相同背景工作角色正在處理相同協調流程的多個工作項目,則儲存體提供者可以藉由快取背景工作角色記憶體中的歷程來優化此流程,這可除去第一個步驟。 此外,它也可以快取執行中的協調器,以除去第二個步驟、歷程亦會重新執行。
快取的典型效果為針對基礎儲存體服務減少 I/O,以及整體改善的輸送量和延遲。 另一方面,快取會增加背景工作角色的記憶體耗用量。
Azure 儲存體提供者和 Netherite 儲存體提供者目前支援執行個體快取。 下列資料表提供比較。
Azure 儲存體提供者 | Netherite 儲存體提供者 | MSSQL 儲存體提供者 | |
---|---|---|---|
執行個體快取 | 支援 (.NET 僅限內含式背景工作角色) |
支援 | 不支援 |
預設設定 | 停用 | 啟用 | n/a |
機制 | 延長工作階段 | 執行個體快取 | n/a |
文件 | 請參閱延長工作階段 | 請參閱實例快取 | n/a |
提示
快取可減少重新執行歷程的頻率,但無法完全將重新執行消除。 在開發協調器時,強烈建議您在停用快取的組態上進行測試。 這個強制重新執行的行為,對於在開發階段偵測協調器函式程式碼強制違規會很有用。
快取機制的比較
提供者使用不同機制來實作快取,並提供不同參數以設定快取行為。
- Azure 儲存體提供者所使用的延長工作階段,會將執行中的協調器保留在記憶體中,直到其閒置一段時間為止。 控制該機制的參數為
extendedSessionsEnabled
和extendedSessionIdleTimeoutInSeconds
。 如需詳細資訊,請參閱 Azure 儲存體提供者文件的延長工作階段一節。
注意
只在 .NET 內含式背景工作角色中支援延長工作階段。
- Netherite 儲存體提供者所使用的執行個體快取會保留背景工作角色記憶體中所有執行個體狀態,其中包括歷程,同時追蹤所使用的記憶體總數。 若快取大小超過
InstanceCacheSizeMB
所設定的限制,則會收回最近所使用最少的執行個體資料。 如果CacheOrchestrationCursors
設定為 true,快取亦會儲存執行中的協調器以及執行個體的狀態。 如需詳細資訊,請參閱 Netherite 儲存體提供者文件的執行個體快取一節。
注意
執行個體快取適用於所有語言 SDK,但 CacheOrchestrationCursors
選項僅適用於 .NET 內含式背景工作角色。
並行節流
單一背景工作角色能同時執行多個工作項目。 這有助於增加平行處理原則,並更具效率地利用背景工作角色。 但若背景工作角色嘗試同時處理過多工作項目,則其可能耗盡可用的資源 (例如 CPU 負載、網路連線數目或可用的記憶體)。
若要確保個別背景工作角色並不過度認可,則可能需要並行個別執行個體以進行節流。 藉由限制在每個背景工作角色上同時執行的函式數目,可以避免耗盡該背景工作角色的資源限制。
注意
該並行節流僅套用於本機,以限制單一背景工作角色目前正在處理的項目。 因此,這些節流不限制系統的總輸送量。
提示
在某些情況下,節流每個背景工作角色並行其實可以增加該系統的輸送量總數。 在每個背景工作角色處理較少工作時,可能會造成縮放控制器新增更多背景工作角色以跟上佇列,進而增加總輸送量。
節流的組態
host.json 檔案中可以設定活動、協調器和實體函式的並行限制。 活動函式的相關設定為 durableTask/maxConcurrentActivityFunctions
,協調器和實體函式的相關設定為 durableTask/maxConcurrentOrchestratorFunctions
。 這些設定可控制在單一背景工作角色中載入記憶體內的協調器、實體或活動函式的數目上限。
注意
協調流程和實體僅在其主動處理事件或作業時,或已啟用執行個體快取時,才會載入記憶體之中。 執行邏輯之後並進行等候時 (亦即,在協調器函式程式碼中叫用 await
(C#) 或 yield
(JavaScript、Python) 陳述式),其可能會從記憶體卸載。 從記憶體卸載的協調流程和實體不算入 maxConcurrentOrchestratorFunctions
節流內。 即使數百萬個協調流程或實體處於「執行中」狀態,也只有在載入作用中記憶體時才算入節流限制內。 同樣地,如果排定活動函式的協調流程等候活動完成執行,則不算入節流內。
Functions 2.0
{
"extensions": {
"durableTask": {
"maxConcurrentActivityFunctions": 10,
"maxConcurrentOrchestratorFunctions": 10
}
}
}
Functions 1.x
{
"durableTask": {
"maxConcurrentActivityFunctions": 10,
"maxConcurrentOrchestratorFunctions": 10
}
}
語言執行階段考量
您選取的語言執行階段可能對函式施以嚴格的並行限制。 例如,以 Python 或 PowerShell 撰寫的 Durable Function 應用程式,在單一 VM 上可能支援一次只執行單一函式。 如果考慮不慎,可能會導致重大的效能問題。 例如,假設協調器展開至 10 個活動,但語言執行階段的並行僅限於一個函式,則 10 個活動函式中有 9 個會停滯,等候機會執行。 此外,因為這 9 個停滯的活動已由 Durable Functions 執行階段載入記憶體中,無法再由其他任何背景工作角色平衡負載。 如果活動函式長時間執行,則問題更棘手。
如果您使用的語言執行階段已限制並行,Durable Functions 並行設定應該更新為符合語言執行階段的並行設定。 這可確保 Durable Functions 執行階段嘗試同時執行的函式不會超過語言執行階段允許的數量,因此,任何擱置的活動得以分散至其他 VM 而平衡負載。 例如,假設您的 Python 應用程式將並行限於 4 個函式 (可能僅在單一語言背景工作處理序上設定 4 個執行緒,或在 4 個背景工作處理序上各設定 1 個執行緒),則您應該將 maxConcurrentOrchestratorFunctions
和 maxConcurrentActivityFunctions
設定為 4。
如需 Python 的詳細資訊和效能建議,請參閱在 Azure Functions 中改善 Python 應用程式的輸送量效能。 此 Python 開發人員參考文件提及的技術可能對 Durable Functions 效能和可擴縮性造成重大影響。
分割區計數
有些儲存體提供者會使用資料分割機制,並且允許指定 partitionCount
參數。
在使用資料分割時,背景工作角色不直接競爭個別的工作項目。 相反地,會先將工作項目分組成 partitionCount
分割區。 然後,這些分割區會指派給背景工作角色。 此負載分配的分割方法有助於減少必要的儲存體存取總數。 此外,它亦能啟用執行個體快取並改善位置,因為它會建立相似性:相同執行個體的所有工作項目皆由相同背景工作角色來處理。
注意
資料分割限制擴增,因為大部分 partitionCount
背景工作角色皆能處理資料分割佇列的工作項目。
下列資料表顯示針對每個儲存體提供者的資料分割佇列,以及針對 partitionCount
參數可允許的範圍和預設值。
Azure 儲存體提供者 | Netherite 儲存體提供者 | MSSQL 儲存體提供者 | |
---|---|---|---|
執行個體訊息 | Partitioned | Partitioned | 未分割 |
活動訊息 | 未分割 | Partitioned | 未分割 |
預設partitionCount |
4 | 12 | n/a |
最大值partitionCount |
16 | 32 | n/a |
文件 | 請參閱協調器擴增 | 請參閱分割計數考量 | n/a |
警告
在建立工作中樞後,就無法再變更分割計數。 因此,建議您將其設定為足夠大的值,以容納工作中樞執行個體在未來擴增的需求。
資料分割計數的組態
參數 partitionCount
可以在 host.json 檔案中進行指定。 下列 host.json 範例程式碼片段將 durableTask/storageProvider/partitionCount
屬性 (或 Durable Functions 1.x 中的 durableTask/partitionCount
) 設定為 3
。
Durable Functions 2.x
{
"extensions": {
"durableTask": {
"storageProvider": {
"partitionCount": 3
}
}
}
}
Durable Functions 1.x
{
"extensions": {
"durableTask": {
"partitionCount": 3
}
}
}
最小化調用延遲的考慮
在正常情況下,叫用要求 (活動、協調器、實體等) 應該相當快速地處理。 不過,不保證任何調用要求的最大延遲,因為它取決於因素,例如:App Service 方案的規模行為類型、並行設定,以及應用程式待辦項目的大小。 因此,建議您投資壓力測試來測量和優化應用程式的尾端延遲。
效能目標
在規劃將 Durable Functions 用於生產應用程式時,請務必在規劃初期考慮效能需求。 某些基本使用方式實例包括:
- 循序活動執行:這個案例說明的協調器函式會一個接一個執行一系列活動函式。 它最類似於函式鏈結範例。
- 平行活動執行:這個案例說明的協調器函式會使用展開傳送、收合傳送模式平行執行許多活動函式。
- 平行處理回應:這個案例是展開傳送、收合傳送模式的第二個部份。 它著重於收合傳送的效能。 請務必請注意,不同於展開傳送,收合傳送是由單一協調器函式執行個體進行,因此只能在單一 VM 上執行。
- 外部事件處理:這個案例代表為外部事件服務的單一協調器函式執行個體 (一次一個)。
- 實體作業處理:此案例會測試單一計數器實體處理一連串作業的速度。
我們會在儲存體提供者的個別文件中,提供這些案例的輸送量號碼。 特別是:
提示
不同於展開傳送,收合傳送作業受限制於單一 VM。 如果您的應用程式使用展開傳送、收合傳送模式,而且您很在意收合傳送效能,請考慮將活動函式展開傳送細分到多個子協調流程。