改善 Azure Functions 的效能與可靠性

本文提供指引來改善無伺服器函式應用程式的效能和可靠性。 如需一組較一般的 Azure Functions 最佳做法,請參閱 Azure Functions 最佳做法

以下是如何使用 Azure Functions 建置及打造無伺服器解決方案的最佳做法。

避免長時間執行的函式

長時間執行的大型函式可能會造成非預期逾時問題。 若要深入了解指定主控方案的逾時,請參閱函數應用程式逾時持續時間

函式可能會因為許多 Node.js 相依性而變大。 匯入相依性也可能會造成載入時間增加,而導致未預期的逾時。 系統會以明確和隱含方式載入相依性。 您的程式碼載入的單一模組可能會載入其本身的其他模組。

在可能時,將大型函式重構為較小的函式集,共用運作並快速傳回回應。 例如,Webhook 或 HTTP 觸發程序函式可能會需要在特定時間限制內的通知回應;而 Webhook 通常需要立即的回應。 您可以將 HTTP 觸發程序承載傳遞到要由佇列觸發程序函式處理的佇列中。 此方法會讓您延後實際工作,並傳回立即回應。

確定背景工作完成

當函式啟動任何工作、回呼、執行緒、程序時,都必須在函式程式碼傳回之前完成這些工作、回呼、執行緒、程序。 由於 Functions 不會追蹤這些背景執行緒,因此不論背景執行緒狀態為何,網站關機都可能會造成函式中非預期的行為。

例如,如果函式啟動背景工作,並在工作完成之前傳回成功的回應,則 Functions 執行階段會將執行視為成功完成,而不論背景工作的結果為何。 如果此背景工作正在執行基本工作,可能會遭到網站關機,讓該工作處於未知狀態。

跨函式通訊

Durable FunctionsAzure Logic Apps 均建置用來管理多個函式之間的狀態轉換和通訊。

若未使用 Durable Functions 或 Logic Apps 來與多個函式整合,則使用儲存體佇列進行跨函式通訊是最佳做法。 主要原因是,比起其他儲存體選項,儲存體佇列更便宜且更容易佈建。

儲存體佇列中個別訊息大小限制在 64 KB。 若您需要在函式之間傳遞更大型的訊息,可使用 Azure 服務匯流排佇列支援標準層大小上限為 256 KB 的訊息,以及進階層大小上限為 100 MB 的訊息。

如果您需要在處理之前篩選訊息,服務匯流排主題會很實用。

事件中樞對於支援大量通訊很有用。

撰寫無狀態函式

如果可能的話,函式應該是無狀態且為等冪。 請將任何必要的狀態資訊與資料建立關聯。 例如,正在處理訂單就可能具有相關聯的 state 成員。 函式本身保持無狀態時,函式可以依據該狀態處理訂單。

特別建議計時器觸發程序使用等冪函式。 例如,若您擁有一天必須執行一次的項目,請寫入該函式,使其可在一天中的任何時間執行,並具有相同的結果。 該函式可在特定日沒有工作時結束。 如果先前的執行無法完成,下一次執行應該會定停止的位置開始。 對於失敗時重試的訊息型繫結而言,這特別重要。 如需詳細資訊,請參閱設計相同輸入的 Azure Functions

編寫防禦性函式

假設您的函式可能隨時會遇到例外狀況。 設計函式,使其能夠在下一次執行期間從先前的失敗點繼續執行。 假設需要執行下列動作的案例︰

  1. 查詢資料庫中的 10,000 個資料列。
  2. 對每個資料列建立佇列訊息,來進一步處理向下一行。

根據系統的複雜程度,您可能會有︰相關的下游服務行為不當、網路中斷或到達配額限制等等。這所有方面都隨時會影響您的函式。 您必須設計您的函式,以對其做好準備。

如果在插入這些項目中的 5,000 個至佇列以進行處理之後發生失敗,您的程式碼如何因應? 追蹤集合中您已完成的項目。 否則,您可能下一次又將它們插入。 此雙重插入會對您的工作流程造成嚴重影響,因此要讓您的函式呈現等冪

如果佇列項目已經過處理,請讓您的函式成為無作業。

利用已針對您在 Azure Functions 平台中所使用元件提供的防禦性措施。 例如,請參閱文件中處理有害的佇列訊息,以了解 Azure 儲存體佇列觸發程序和繫結

針對 HTTP 型函式,請考慮使用 Azure API 管理的 API 版本控制策略。 例如,若您必須更新 HTTP 函數應用程式,請將新的更新部署到不同的函數應用程式,並使用 API 管理修訂或版本將用戶端導向至新版本或修訂。 所有用戶端都使用版本或修訂,而且先前的函數應用程式上不會再執行任何執行之後,您就可以取消佈建先前的函數應用程式。

函式組織最佳做法

作為解決方案的一部分,您可以開發及發佈多個函式。 這些函式通常會合併成單一函數應用程式,但也可以在不同的函數應用程式中執行。 在進階與專用 (App Service) 主控方案中,多個函數應用程式也可以藉由在同一個方案中執行來共用相同的資源。 如何將函式與函數應用程式分組,可能會影響整體解決方案的效能、規模、設定、部署與安全性。 沒有適用於每個案例的規則,因此在規劃及開發您的函式時,請考慮此節中的資訊。

組織函式以便提升效能及調整規模

您建立的每個函式都有記憶體使用量。 雖然此使用量通常很小,但在函數應用程式中擁有太多函式,可能會導致新執行個體上的應用程式啟動速度變慢。 這也表示函數應用程式的整體記憶體使用量可能較高。 很難說單一應用程式中應該有多少函式,這取決於您的特定工作負載。 然而,若您的函式在記憶體中儲存大量資料,請考慮在單一應用程式中擁有較少的函式。

若您在單一進階方案或專用 (App Service) 方案中執行多個函數應用程式,則這些應用程式都會共用配置給方案的相同資源。 若您有一個記憶體需求高於其他函數應用程式的函數應用程式,則其會在部署應用程式的每個執行個體上使用不成比例的記憶體資源量。 因為這可能會讓每個執行個體上的其他應用程式使用較少的記憶體,所以您可能會想要在自己的個別主控方案中執行高記憶體使用函數應用程式,就像這樣。

注意

使用取用方案時,建議一律將每個應用程式放在自己的方案中,因為應用程式仍然會獨立調整。 如需詳細資訊,請參閱相同方案中的多個應用程式

請考慮是否要使用不同的負載設定檔將函式分組。 例如,若您有一個處理數千個佇列訊息的函式,而另一個函式只會偶爾呼叫但記憶體需求很高,您可能會想要將這些函式部署在個別的函數應用程式中,使其取得自己的資源集,而且彼此獨立調整。

組織設定與部署的函式

函數應用程式有 host.json 檔案,可用於設定函式觸發程序與 Azure Functions 執行階段的進階行為。 host.json 檔案的變更會套用至應用程式中的所有函式。 若您有一些需要自訂設定的函式,請考慮將這些函式移至其自身的函數應用程式。

本機專案中的所有函式都會一起部署為 Azure 中函數應用程式的一組檔案。 您可能需要個別部署個別函式,或使用某些函式的部署位置等功能,而不是其他功能。 在這種情況下,您應該將這些函式 (位於不同的程式碼專案中) 部署至不同的函數應用程式。

依權限組織函式

儲存在應用程式設定中的連接字串和其他認證,會為函式應用程式中的所有函式提供與相關聯資源中相同的權限集。 請考慮藉由將未使用那些認證的函式移至個別函式應用程式,來將可存取特定認證的函式數目降至最低。 您一律可以使用函式鏈結之類的技術,在不同函式應用程式的函式之間傳遞資料。

延展性最佳做法

有數個因素會影響函數應用程式執行個體的調整方式。 函式調整文件中會提供詳細資料。 以下是一些最佳做法,可確保函式應用程式的最佳延展性。

共用及管理連線

盡可能重複使用與外部資源的連線。 請參閱如何管理 Azure Functions 中的連線

避免共用儲存體帳戶

當您建立函式應用程式時,必須將其與儲存體帳戶建立關聯性。 儲存體帳戶連線會在 AzureWebJobsStorage application setting 中維護。

若要盡可能提高效能,每個函數應用程式應使用個別的儲存體帳戶。 當您有 Durable Functions 或事件中樞觸發的函式時,這一點特別重要,這兩個函式都會產生大量的儲存體交易。 當您的應用程式邏輯與 Azure 儲存體互動 (不論是直接 (使用儲存體 SDK) 或透過其中一個儲存體繫結) 時,您應該使用專用的儲存體帳戶。 例如,若有事件中樞觸發的函式將一些資料寫入 Blob 儲存體,請使用兩個儲存體帳戶,一個用於函數應用程式,另一個用於函式所儲存的 Blob。

請勿在相同的函式應用程式中混用測試和生產程式碼

函式應用程式內的 Functions 會共用資源。 例如,共用記憶體。 如果您在生產環境中使用函式應用程式,請勿對它新增與測試相關的函式和資源。 在實際執行程式碼執行期間可能導致發生未預期的額外負荷。

對於在實際執行函式應用程式中載入的項目,請務必小心。 記憶體會在應用程式中的每個函式間平均分配。

若您在多個 .Net 函式中參考某個共用組件,請將其置於常用的共用資料夾中。 否則,很容易不小心在函式之間部署同樣是二進位但擁有不同行為的多個版本。

請勿在生產環境程式碼中使用詳細資訊記錄,這會對效能造成負面影響。

使用非同步程式碼,但避免封鎖呼叫

非同步程式設計是建議的最佳做法,特別是在涉及封鎖 I/O 作業時。

在 C# 中,請務必避免在 Task 執行個體上參考 Result 屬性或呼叫 Wait 方法。 這個方法可能會導致執行緒耗盡。

提示

如果您打算使用 HTTP 或 WebHook 的繫結,請做好規劃,以免因為 HttpClient 具現化不當而耗盡連接埠。 如需詳細資訊,請參閱如何管理 Azure Functions 中的連線

使用多個背景工作處理序

根據預設,Functions 的任何主機執行個體都會使用單一背景工作處理序。 若要改善效能 (特別是使用 Python 等單一執行緒的執行階段),請使用 FUNCTIONS_WORKER_PROCESS_COUNT 來增加每個主機的背景工作處理序數目 (最多 10 個)。 Azure Functions 接著會嘗試在這些背景工作中平均散發同時函式叫用。

FUNCTIONS_WORKER_PROCESS_COUNT 適用於 Functions 在擴增應用程式以符合需求時所建立的每個主機。

盡可能批次接收訊息

某些觸發程序 (如事件中樞) 能夠在單一引動過程中接收一批訊息。 分批處理訊息的效能比較好。 如 host.json 參考文件所述,您可以在 host.json 檔案中設定批次大小上限。

針對 C# 函式,您可以將類型變更為強型別陣列。 例如,方法簽章可能是 EventData[] sensorEvent,而不是 EventData sensorEvent。 若為其他語言,您必須在 function.json 中將基數屬性明確設定為 many,才能啟用批次處理,如此處所示

設定主機的行為,更妥善處理並行作業

函式應用程式中的 host.json 檔案能夠設定主機執行階段和觸發程序行為。 除了批次處理行為,您可以管理數個觸發程序的並行作業。 經常調整這些選項中的值,可協助每個執行個體針對所叫用函式的需求進行適當調整。

host.json 檔案中的設定會套用至應用程式內的所有函式,以及函式的「單一執行個體」內。 例如,若您的函數應用程式具有兩個 HTTP 函式,且 maxConcurrentRequests 要求設定為 25,則對任一 HTTP 觸發程序的要求會計入共用的 25 個並行要求中。 若將該函數應用程式調整為 10 個執行個體,則這十個函式可有效地允許 250 個並行要求 (10 個執行個體 * 每個執行個體的 25 個並行要求)。

host.json 設定文章中可以找到其他主機設定選項。

下一步

如需詳細資訊,請參閱以下資源: