Zwiększanie wydajności i niezawodności Azure Functions

Ten artykuł zawiera wskazówki dotyczące poprawy wydajności i niezawodności aplikacji funkcji bezserwerowych . Aby uzyskać bardziej ogólny zestaw najlepszych rozwiązań Azure Functions, zobacz Azure Functions najlepszych rozwiązań.

Poniżej przedstawiono najlepsze rozwiązania dotyczące tworzenia i tworzenia rozwiązań bezserwerowych przy użyciu Azure Functions.

Unikaj długotrwałych funkcji

Duże, długotrwałe funkcje mogą powodować nieoczekiwane problemy z limitem czasu. Aby dowiedzieć się więcej na temat limitów czasu dla danego planu hostingu, zobacz limit czasu trwania aplikacji funkcji.

Funkcja może stać się duża z powodu wielu Node.js zależności. Importowanie zależności może również powodować zwiększone czasy ładowania, które powodują nieoczekiwane przekroczenia limitu czasu. Zależności są ładowane jawnie i niejawnie. Pojedynczy moduł załadowany przez kod może załadować własne dodatkowe moduły.

Jeśli to możliwe, refaktoryzacja dużych funkcji w mniejszych zestawach funkcji, które współpracują ze sobą i zwracają odpowiedzi szybko. Na przykład element webhook lub funkcja wyzwalacza HTTP może wymagać odpowiedzi potwierdzenia w ramach określonego limitu czasu; często zdarza się, że elementy webhook wymagają natychmiastowej odpowiedzi. Ładunek wyzwalacza HTTP można przekazać do kolejki do przetworzenia przez funkcję wyzwalacza kolejki. Takie podejście pozwala odroczyć rzeczywistą pracę i zwrócić natychmiastową odpowiedź.

Upewnij się, że zadania w tle są ukończone

Gdy funkcja uruchamia wszystkie zadania, wywołania zwrotne, wątki, procesy, muszą zostać ukończone przed zwróceniem kodu funkcji. Ponieważ funkcje nie śledzą tych wątków w tle, zamknięcie lokacji może wystąpić niezależnie od stanu wątku w tle, co może powodować niezamierzone zachowanie w funkcjach.

Jeśli na przykład funkcja uruchamia zadanie w tle i zwraca pomyślną odpowiedź przed ukończeniem zadania, środowisko uruchomieniowe usługi Functions uznaje wykonanie za zakończone pomyślnie, niezależnie od wyniku zadania w tle. Jeśli to zadanie w tle wykonuje istotną pracę, może zostać wywłaszczone przez zamknięcie lokacji, pozostawiając tę pracę w nieznanym stanie.

Komunikacja między funkcjami

Durable Functions i Azure Logic Apps są tworzone w celu zarządzania przejściami stanu i komunikacją między wieloma funkcjami.

Jeśli nie używasz Durable Functions lub usługi Logic Apps do integracji z wieloma funkcjami, najlepiej używać kolejek magazynu do komunikacji między funkcjami. Głównym powodem jest to, że kolejki magazynu są tańsze i znacznie łatwiejsze do aprowizacji niż inne opcje magazynu.

Poszczególne komunikaty w kolejce magazynu są ograniczone do 64 KB. Jeśli musisz przekazać większe komunikaty między funkcjami, kolejka Azure Service Bus może służyć do obsługi rozmiarów komunikatów do 256 KB w warstwie Standardowa i do 100 MB w warstwie Premium.

Tematy usługi Service Bus są przydatne, jeśli wymagane jest filtrowanie komunikatów przed przetworzeniem.

Centra zdarzeń są przydatne do obsługi dużej komunikacji zbiorczej.

Funkcje zapisu, które mają być bezstanowe

Funkcje powinny być bezstanowe i idempotentne, jeśli to możliwe. Skojarz wszystkie wymagane informacje o stanie z danymi. Na przykład przetwarzane zamówienie prawdopodobnie będzie miało skojarzony state element członkowski. Funkcja może przetwarzać kolejność na podstawie tego stanu, podczas gdy sama funkcja pozostaje bezstanowa.

Funkcje idempotentne są szczególnie zalecane w przypadku wyzwalaczy czasomierza. Jeśli na przykład masz coś, co absolutnie musi działać raz dziennie, napisz go, aby mógł działać w dowolnym momencie w ciągu dnia z tymi samymi wynikami. Funkcja może zakończyć działanie, gdy nie ma pracy dla określonego dnia. Ponadto jeśli poprzednie uruchomienie nie powiodło się, następnym uruchomieniem powinno zostać odebrane miejsce, w którym zostało przerwane. Jest to szczególnie ważne w przypadku powiązań opartych na komunikatach, które ponawiają próbę po awarii. Aby uzyskać więcej informacji, zobacz Projektowanie Azure Functions pod kątem identycznych danych wejściowych.

Pisanie funkcji defensywnych

Załóżmy, że funkcja może w dowolnym momencie napotkać wyjątek. Zaprojektuj funkcje z możliwością kontynuowania z poprzedniego punktu awarii podczas następnego wykonywania. Rozważmy scenariusz, który wymaga następujących akcji:

  1. Wykonaj zapytanie o 10 000 wierszy w bazie danych.
  2. Utwórz komunikat kolejki dla każdego z tych wierszy, aby przetworzyć go w dół.

W zależności od tego, jak złożony jest system, może istnieć: zaangażowane usługi podrzędne zachowują się źle, awarie sieci lub osiągnięto limity przydziału itp. Wszystkie te funkcje mogą mieć wpływ na funkcję w dowolnym momencie. Musisz zaprojektować funkcje, aby je przygotować.

Jak kod reaguje, jeśli wystąpi błąd po wstawieniu 5000 tych elementów do kolejki na potrzeby przetwarzania? Śledź elementy w zestawie, który został ukończony. W przeciwnym razie możesz je ponownie wstawić ponownie przy następnym uruchomieniu. Ta podwójna wstawienie może mieć poważny wpływ na przepływ pracy, dzięki czemu funkcja jest idempotentna.

Jeśli element kolejki został już przetworzony, zezwól funkcji na brak operacji.

Skorzystaj z środków defensywnych już dostępnych dla składników używanych na platformie Azure Functions. Na przykład zobacz Obsługa zatrutych komunikatów kolejki w dokumentacji wyzwalaczy i powiązań kolejki usługi Azure Storage.

W przypadku funkcji opartych na protokole HTTP należy rozważyć strategie przechowywania wersji interfejsu API za pomocą usługi Azure API Management. Jeśli na przykład musisz zaktualizować aplikację funkcji opartą na protokole HTTP, wdróż nową aktualizację w oddzielnej aplikacji funkcji i użyj API Management poprawek lub wersji, aby skierować klientów do nowej wersji lub poprawki. Gdy wszyscy klienci korzystają z wersji lub poprawki i nie pozostanie więcej wykonań w poprzedniej aplikacji funkcji, możesz zrezygnować z aprowizacji poprzedniej aplikacji funkcji.

Najlepsze rozwiązania dotyczące organizacji funkcji

W ramach rozwiązania możesz opracowywać i publikować wiele funkcji. Te funkcje są często łączone w jedną aplikację funkcji, ale mogą również działać w oddzielnych aplikacjach funkcji. W planach hostingu Premium i dedykowanych (App Service) wiele aplikacji funkcji może również współdzielić te same zasoby, uruchamiając je w tym samym planie. Sposób grupowania funkcji i aplikacji funkcji może mieć wpływ na wydajność, skalowanie, konfigurację, wdrażanie i zabezpieczenia ogólnego rozwiązania. Nie istnieją reguły, które mają zastosowanie do każdego scenariusza, dlatego podczas planowania i opracowywania funkcji należy wziąć pod uwagę informacje przedstawione w tej sekcji.

Organizowanie funkcji pod kątem wydajności i skalowania

Każda utworzona funkcja ma ślad pamięci. Chociaż ten ślad jest zwykle mały, zbyt wiele funkcji w aplikacji funkcji może prowadzić do wolniejszego uruchamiania aplikacji w nowych wystąpieniach. Oznacza to również, że ogólne użycie pamięci aplikacji funkcji może być wyższe. Trudno powiedzieć, ile funkcji powinno znajdować się w jednej aplikacji, która zależy od konkretnego obciążenia. Jeśli jednak funkcja przechowuje dużo danych w pamięci, rozważ użycie mniejszej liczby funkcji w jednej aplikacji.

Jeśli uruchamiasz wiele aplikacji funkcji w ramach jednego planu Premium lub dedykowanego (App Service), wszystkie te aplikacje współużytkują te same zasoby przydzielone do planu. Jeśli masz jedną aplikację funkcji, która ma znacznie większe wymaganie dotyczące pamięci niż inne, używa nieproporcjonalnej ilości zasobów pamięci w każdym wystąpieniu, do którego jest wdrożona aplikacja. Ponieważ może to spowodować pozostawienie mniejszej ilości pamięci dla innych aplikacji w każdym wystąpieniu, możesz chcieć uruchomić aplikację funkcji o wysokiej ilości pamięci, taką jak ta w osobnym planie hostingu.

Uwaga

W przypadku korzystania z planu Zużycie zalecamy zawsze umieszczenie każdej aplikacji we własnym planie, ponieważ mimo to aplikacje są skalowane niezależnie. Aby uzyskać więcej informacji, zobacz Wiele aplikacji w tym samym planie.

Zastanów się, czy chcesz grupować funkcje z różnymi profilami obciążenia. Jeśli na przykład masz funkcję, która przetwarza wiele tysięcy komunikatów w kolejce, a inna, która jest wywoływana tylko od czasu do czasu, ale ma wysokie wymagania dotyczące pamięci, warto wdrożyć je w oddzielnych aplikacjach funkcji, aby uzyskać własne zestawy zasobów i skalować niezależnie od siebie.

Organizowanie funkcji na potrzeby konfiguracji i wdrażania

Aplikacje funkcji mają host.json plik, który służy do konfigurowania zaawansowanego zachowania wyzwalaczy funkcji i środowiska uruchomieniowego Azure Functions. Zmiany w host.json pliku mają zastosowanie do wszystkich funkcji w aplikacji. Jeśli masz pewne funkcje, które wymagają konfiguracji niestandardowych, rozważ przeniesienie ich do własnej aplikacji funkcji.

Wszystkie funkcje w projekcie lokalnym są wdrażane razem jako zestaw plików do aplikacji funkcji na platformie Azure. Może być konieczne oddzielne wdrożenie poszczególnych funkcji lub użycie funkcji, takich jak miejsca wdrożenia dla niektórych funkcji, a nie innych. W takich przypadkach należy wdrożyć te funkcje (w oddzielnych projektach kodu) w różnych aplikacjach funkcji.

Organizowanie funkcji według uprawnień

Parametry połączenia i inne poświadczenia przechowywane w ustawieniach aplikacji udostępniają wszystkim funkcjom w aplikacji funkcji ten sam zestaw uprawnień w skojarzonym zasobie. Rozważ zminimalizowanie liczby funkcji z dostępem do określonych poświadczeń przez przeniesienie funkcji, które nie używają tych poświadczeń do oddzielnej aplikacji funkcji. Zawsze można użyć technik, takich jak łańcuch funkcji , aby przekazywać dane między funkcjami w różnych aplikacjach funkcji.

Najlepsze rozwiązania dotyczące skalowalności

Istnieje wiele czynników wpływających na sposób skalowania wystąpień aplikacji funkcji. Szczegółowe informacje znajdują się w dokumentacji dotyczącej skalowania funkcji. Poniżej przedstawiono najlepsze rozwiązania w celu zapewnienia optymalnej skalowalności aplikacji funkcji.

Udostępnianie połączeń i zarządzanie nimi

Ponowne używanie połączeń z zasobami zewnętrznymi zawsze, gdy jest to możliwe. Zobacz, jak zarządzać połączeniami w Azure Functions.

Unikanie udostępniania kont magazynu

Podczas tworzenia aplikacji funkcji należy ją skojarzyć z kontem magazynu. Połączenie konta magazynu jest obsługiwane w ustawieniu aplikacji AzureWebJobsStorage.

Aby zmaksymalizować wydajność, użyj oddzielnego konta magazynu dla każdej aplikacji funkcji. Jest to szczególnie ważne w przypadku Durable Functions lub funkcji wyzwalanych przez centrum zdarzeń, które generują dużą liczbę transakcji magazynu. Gdy logika aplikacji współdziała z usługą Azure Storage, bezpośrednio (przy użyciu zestawu SDK usługi Storage) lub za pomocą jednego z powiązań magazynu, należy użyć dedykowanego konta magazynu. Jeśli na przykład masz funkcję wyzwalaną przez centrum zdarzeń, zapisując niektóre dane w magazynie obiektów blob, użyj dwóch kont magazynu — jednej dla aplikacji funkcji, a drugiej dla obiektów blob przechowywanych przez funkcję.

Nie mieszaj kodu testowego i produkcyjnego w tej samej aplikacji funkcji

Funkcje w aplikacji funkcji udostępniają zasoby. Na przykład pamięć jest współużytkowana. Jeśli używasz aplikacji funkcji w środowisku produkcyjnym, nie dodawaj do niej funkcji i zasobów związanych z testami. Może to spowodować nieoczekiwane obciążenie podczas wykonywania kodu produkcyjnego.

Zachowaj ostrożność podczas ładowania aplikacji funkcji produkcyjnych. Pamięć jest średnią dla każdej funkcji w aplikacji.

Jeśli masz zestaw udostępniony, do którego odwołujesz się w wielu funkcjach platformy .NET, umieść go w typowym folderze udostępnionym. W przeciwnym razie można przypadkowo wdrożyć wiele wersji tego samego pliku binarnego, które zachowują się inaczej między funkcjami.

Nie używaj pełnego rejestrowania w kodzie produkcyjnym, który ma negatywny wpływ na wydajność.

Używanie kodu asynchronicznego, ale unikanie blokowania wywołań

Programowanie asynchroniczne jest zalecanym najlepszym rozwiązaniem, szczególnie w przypadku blokowania operacji we/wy.

W języku C#należy zawsze unikać odwoływania Result się do właściwości lub wywoływania Wait metody w wystąpieniu Task . Takie podejście może prowadzić do wyczerpania wątków.

Porada

Jeśli planujesz używać powiązań HTTP lub WebHook, zaplanuj uniknięcie wyczerpania portów, które mogą być spowodowane niewłaściwym wystąpieniem HttpClientelementu . Aby uzyskać więcej informacji, zobacz Jak zarządzać połączeniami w Azure Functions.

Korzystanie z wielu procesów roboczych

Domyślnie każde wystąpienie hosta dla usługi Functions używa jednego procesu roboczego. Aby zwiększyć wydajność, szczególnie w przypadku środowiska uruchomieniowego z jednym wątkiem, takiego jak Python, użyj FUNCTIONS_WORKER_PROCESS_COUNT , aby zwiększyć liczbę procesów roboczych na hosta (do 10). Azure Functions następnie próbuje równomiernie dystrybuować równoczesne wywołania funkcji między tymi procesami roboczymi.

Ustawienie FUNCTIONS_WORKER_PROCESS_COUNT dotyczy każdego hosta tworzonego przez rozwiązanie Functions podczas skalowania aplikacji w poziomie w celu zaspokojenia popytu.

Odbieranie komunikatów w partii zawsze, gdy jest to możliwe

Niektóre wyzwalacze, takie jak centrum zdarzeń, umożliwiają odbieranie partii komunikatów w ramach pojedynczego wywołania. Komunikaty wsadowe mają znacznie lepszą wydajność. Maksymalny rozmiar partii w host.json pliku można skonfigurować zgodnie z opisem w dokumentacji referencyjnej pliku host.json

W przypadku funkcji języka C# można zmienić typ na silnie typową tablicę. Na przykład zamiast EventData sensorEvent podpisu metody może to być EventData[] sensorEvent. W przypadku innych języków należy jawnie ustawić właściwość kardynalności w pliku function.jsonmany , aby włączyć przetwarzanie wsadowe , jak pokazano tutaj.

Konfigurowanie zachowań hosta pod kątem lepszej obsługi współbieżności

Plik host.json w aplikacji funkcji umożliwia konfigurację środowiska uruchomieniowego hosta i zachowania wyzwalacza. Oprócz zachowań przetwarzania wsadowego można zarządzać współbieżnością wielu wyzwalaczy. Często dostosowywanie wartości w tych opcjach może pomóc każdemu wystąpieniu odpowiednio skalować wymagania wywoływanych funkcji.

Ustawienia w pliku host.json mają zastosowanie we wszystkich funkcjach w aplikacji w ramach pojedynczego wystąpienia funkcji. Jeśli na przykład masz aplikację funkcji z dwiema funkcjami HTTP i maxConcurrentRequests żądaniami ustawionymi na 25, żądanie do jednego z wyzwalaczy HTTP będzie liczone do współużytkowanych 25 współbieżnych żądań. Gdy ta aplikacja funkcji jest skalowana do 10 wystąpień, dziesięć funkcji skutecznie zezwala na 250 równoczesnych żądań (10 wystąpień * 25 współbieżnych żądań na wystąpienie).

Inne opcje konfiguracji hosta znajdują się w artykule dotyczącym konfiguracji host.json.

Następne kroki

Więcej informacji można znaleźć w następujących zasobach: