Wydajność i skalowanie w usłudze Durable Functions (Azure Functions)

Aby zoptymalizować wydajność i skalowalność, ważne jest, aby zrozumieć unikatowe cechy skalowania rozszerzenia Durable Functions. W tym artykule wyjaśniono, jak procesy robocze są skalowane na podstawie obciążenia i jak można dostroić różne parametry.

Skalowanie procesów roboczych

Podstawową zaletą koncepcji centrum zadań jest to, że liczba procesów roboczych, które przetwarzają elementy robocze centrum zadań, może być stale dostosowywana. W szczególności aplikacje mogą dodawać więcej procesów roboczych (skalować w poziomie), jeśli praca musi być przetwarzana szybciej, i może usuwać procesy robocze (skalować w poziomie), jeśli nie ma wystarczającej ilości pracy, aby zapewnić pracownikom zajętość. Istnieje nawet możliwość skalowania do zera , jeśli centrum zadań jest całkowicie bezczynne. W przypadku skalowania do zera nie ma żadnych procesów roboczych; tylko kontroler skalowania i magazyn muszą pozostać aktywne.

Na poniższym diagramie przedstawiono tę koncepcję:

Worker scaling diagram

Automatyczne skalowanie

Podobnie jak w przypadku wszystkich funkcji platformy Azure działających w planach Zużycie i Elastic Premium, rozszerzenie Durable Functions obsługuje automatyczne skalowanie za pośrednictwem kontrolera skalowania usługi Azure Functions. Kontroler skalowania monitoruje czas oczekiwania komunikatów i zadań przed ich przetworzeniem. Na podstawie tych opóźnień może zdecydować, czy dodać lub usunąć procesy robocze.

Uwaga

Począwszy od rozszerzenia Durable Functions 2.0, aplikacje funkcji można skonfigurować do uruchamiania w punktach końcowych usługi chronionej przez sieć wirtualną w ramach planu Elastic Premium. W tej konfiguracji wyzwalacze rozszerzenia Durable Functions inicjują żądania skalowania zamiast kontrolera skalowania. Aby uzyskać więcej informacji, zobacz Monitorowanie skalowania w czasie wykonywania.

W ramach planu Premium automatyczne skalowanie może pomóc w utrzymaniu liczby procesów roboczych (a tym samym kosztach operacyjnych) w przybliżeniu proporcjonalnych do obciążenia, którego doświadcza aplikacja.

Użycie procesora CPU

Funkcje programu Orchestrator są wykonywane w jednym wątku, aby upewnić się, że wykonywanie może być deterministyczne w wielu powtórzeń. Ze względu na to jednowątkowe wykonywanie ważne jest, aby wątki funkcji orkiestratora nie wykonywały zadań intensywnie korzystających z procesora CPU, operacji we/wy lub blokuj z jakiegokolwiek powodu. Każda praca, która może wymagać operacji we/wy, blokowania lub wielu wątków, powinna zostać przeniesiona do funkcji działania.

Funkcje działań mają takie same zachowania jak zwykłe funkcje wyzwalane przez kolejkę. Mogą bezpiecznie wykonywać operacje we/wy, wykonywać operacje intensywnie korzystające z procesora CPU i używać wielu wątków. Ponieważ wyzwalacze działań są bezstanowe, mogą swobodnie skalować w poziomie do niezwiązanej liczby maszyn wirtualnych.

Funkcje jednostki są również wykonywane w jednym wątku, a operacje są przetwarzane pojedynczo. Jednak funkcje jednostek nie mają żadnych ograniczeń dotyczących typu kodu, który można wykonać.

Limity czasu funkcji

Funkcje działania, orkiestratora i jednostki podlegają tym samym limitom czasu funkcji co wszystkie funkcje platformy Azure. Ogólnie rzecz biorąc, rozszerzenie Durable Functions traktuje limity czasu funkcji w taki sam sposób, jak nieobsługiwane wyjątki zgłaszane przez kod aplikacji.

Jeśli na przykład przekroczono limit czasu działania, wykonanie funkcji jest rejestrowane jako błąd, a orkiestrator jest powiadamiany i obsługuje limit czasu, podobnie jak w przypadku każdego innego wyjątku: ponawianie prób odbywa się, jeśli zostanie określone przez wywołanie, lub może zostać wykonana procedura obsługi wyjątków.

Przetwarzanie wsadowe operacji jednostki

Aby zwiększyć wydajność i obniżyć koszty, pojedynczy element roboczy może wykonać całą partię operacji jednostki. W przypadku planów zużycia każda partia jest następnie rozliczana jako pojedyncze wykonanie funkcji.

Domyślnie maksymalny rozmiar partii wynosi 50 dla planów zużycia i 5000 dla wszystkich innych planów. Maksymalny rozmiar partii można również skonfigurować w pliku host.json . Jeśli maksymalny rozmiar partii wynosi 1, przetwarzanie wsadowe jest skutecznie wyłączone.

Uwaga

Jeśli wykonywanie operacji poszczególnych jednostek trwa długo, może być korzystne ograniczenie maksymalnego rozmiaru partii w celu zmniejszenia ryzyka przekroczenia limitu czasu funkcji, w szczególności w przypadku planów zużycia.

Buforowanie wystąpień

Ogólnie rzecz biorąc, aby przetworzyć element roboczy aranżacji, proces roboczy musi mieć oba

  1. Pobierz historię aranżacji.
  2. Powtórz kod orkiestratora przy użyciu historii.

Jeśli ten sam proces roboczy przetwarza wiele elementów roboczych dla tej samej aranżacji, dostawca magazynu może zoptymalizować ten proces, buforując historię w pamięci procesu roboczego, co eliminuje pierwszy krok. Co więcej, może buforować orkiestrator mid-execution, który eliminuje drugi krok, powtarzanie historii, jak również.

Typowy efekt buforowania polega na zmniejszeniu liczby operacji we/wy względem bazowej usługi magazynu oraz ogólnej zwiększonej przepływności i opóźnienia. Z drugiej strony buforowanie zwiększa zużycie pamięci w ramach procesu roboczego.

Buforowanie wystąpień jest obecnie obsługiwane przez dostawcę usługi Azure Storage i dostawcę magazynu Netherite. Poniższa tabela zawiera porównanie.

Dostawca usługi Azure Storage Dostawca magazynu netherite Dostawca magazynu MSSQL
Buforowanie wystąpień Obsługiwane
(Tylko proces roboczy platformy.NET)
Obsługiwane Nieobsługiwane
Ustawienie domyślne Disabled Włączony nie dotyczy
Mechanizm Sesje rozszerzone Pamięć podręczna wystąpień nie dotyczy
Dokumentacja Zobacz Sesje rozszerzone Zobacz Pamięć podręczna wystąpień nie dotyczy

Napiwek

Buforowanie może zmniejszyć częstotliwość odtwarzania historii, ale nie może całkowicie wyeliminować odtwarzania. Podczas opracowywania orkiestratorów zdecydowanie zalecamy przetestowanie ich w konfiguracji, która wyłącza buforowanie. To wymuszone zachowanie odtwarzania może być przydatne do wykrywania naruszeń ograniczeń kodu funkcji orkiestratora w czasie programowania.

Porównanie mechanizmów buforowania

Dostawcy używają różnych mechanizmów do implementowania buforowania i oferują różne parametry w celu skonfigurowania zachowania buforowania.

  • Sesje rozszerzone, używane przez dostawcę usługi Azure Storage, przechowują orkiestratory wykonywania w połowie w pamięci, dopóki nie będą w stanie bezczynności przez jakiś czas. Parametry do kontrolowania tego mechanizmu to extendedSessionsEnabled i extendedSessionIdleTimeoutInSeconds. Aby uzyskać więcej informacji, zobacz sekcję Sesje rozszerzone dokumentacji dostawcy usługi Azure Storage.

Uwaga

Sesje rozszerzone są obsługiwane tylko w procesie roboczym platformy .NET.

  • Pamięć podręczna wystąpienia używana przez dostawcę magazynu Netherite przechowuje stan wszystkich wystąpień, w tym ich historii, w pamięci procesu roboczego, przy jednoczesnym śledzeniu całkowitej używanej pamięci. Jeśli rozmiar pamięci podręcznej przekroczy limit skonfigurowany przez InstanceCacheSizeMBusługę , co najmniej ostatnio używane dane wystąpienia zostanie wykluczone. Jeśli CacheOrchestrationCursors jest ustawiona wartość true, pamięć podręczna przechowuje również koordynatorów wykonywania w połowie wykonywania wraz ze stanem wystąpienia. Aby uzyskać więcej informacji, zobacz sekcję Pamięć podręczna wystąpień dokumentacji dostawcy magazynu Netherite.

Uwaga

Pamięci podręczne wystąpień działają dla wszystkich zestawów SDK języka, ale CacheOrchestrationCursors opcja jest dostępna tylko dla procesu roboczego w procesie przetwarzania platformy .NET.

Ograniczenia współbieżności

Jedno wystąpienie procesu roboczego może jednocześnie wykonywać wiele elementów roboczych . Pomaga to zwiększyć równoległość i wydajniej wykorzystywać pracowników. Jeśli jednak proces roboczy próbuje przetworzyć zbyt wiele elementów roboczych w tym samym czasie, może wyczerpać dostępne zasoby, takie jak obciążenie procesora CPU, liczba połączeń sieciowych lub dostępna pamięć.

Aby zagwarantować, że pojedynczy proces roboczy nie zostanie nadmiernie przydzielony, może być konieczne ograniczenie współbieżności dla poszczególnych wystąpień. Ograniczając liczbę funkcji, które są jednocześnie uruchomione dla każdego procesu roboczego, możemy uniknąć wyczerpania limitów zasobów dla tego procesu roboczego.

Uwaga

Ograniczenia współbieżności mają zastosowanie tylko lokalnie, aby ograniczyć to, co jest obecnie przetwarzane dla każdego procesu roboczego. W związku z tym te ograniczenia nie ograniczają całkowitej przepływności systemu.

Napiwek

W niektórych przypadkach ograniczanie współbieżności poszczególnych procesów roboczych może rzeczywiście zwiększyć całkowitą przepływność systemu. Może się tak zdarzyć, gdy każdy proces roboczy zajmuje mniej pracy, powodując, że kontroler skalowania doda więcej procesów roboczych, aby nadążyć za kolejkami, co zwiększa łączną przepływność.

Konfiguracja ograniczeń

Limity współbieżności działania, orkiestratora i funkcji jednostki można skonfigurować w pliku host.json . Odpowiednie ustawienia są durableTask/maxConcurrentActivityFunctions przeznaczone dla funkcji działania oraz durableTask/maxConcurrentOrchestratorFunctions dla funkcji orkiestratora i jednostki. Te ustawienia kontrolują maksymalną liczbę funkcji orkiestratora, jednostki lub działania, które są ładowane do pamięci w jednym procesie roboczym.

Uwaga

Orkiestracje i jednostki są ładowane do pamięci tylko wtedy, gdy aktywnie przetwarzają zdarzenia lub operacje lub jeśli buforowanie wystąpień jest włączone. Po wykonaniu logiki i oczekiwaniu (tj. naciśnięciu await instrukcji (C#) lub yield (JavaScript, Python) w kodzie funkcji orkiestratora mogą zostać wyładowane z pamięci. Orkiestracje i jednostki, które są zwalniane z pamięci, nie są liczone do maxConcurrentOrchestratorFunctions ograniczenia przepustowości. Nawet jeśli miliony orkiestracji lub jednostek są w stanie "Uruchomiono", są one liczone tylko do limitu ograniczania przepustowości, gdy są ładowane do aktywnej pamięci. Orkiestracja, która planuje działanie, podobnie nie liczy się w kierunku ograniczenia, jeśli orkiestracja czeka na zakończenie wykonywania działania.

Functions 2.0

{
  "extensions": {
    "durableTask": {
      "maxConcurrentActivityFunctions": 10,
      "maxConcurrentOrchestratorFunctions": 10
    }
  }
}

Functions w wersji 1.x

{
  "durableTask": {
    "maxConcurrentActivityFunctions": 10,
    "maxConcurrentOrchestratorFunctions": 10
  }
}

Zagadnienia dotyczące środowiska uruchomieniowego języka

Wybrane środowisko uruchomieniowe języka może nakładać ścisłe ograniczenia współbieżności lub funkcje. Na przykład aplikacje durable function napisane w języku Python lub PowerShell mogą obsługiwać uruchamianie tylko jednej funkcji na jednej maszynie wirtualnej. Może to spowodować znaczne problemy z wydajnością, jeśli nie zostały dokładnie uwzględnione. Jeśli na przykład fani orkiestratora do 10 działań, ale środowisko uruchomieniowe języka ogranicza współbieżność tylko do jednej funkcji, 9 z 10 funkcji działania będzie zablokowanych w oczekiwaniu na szansę uruchomienia. Ponadto te 9 zablokowanych działań nie będzie mogło zostać zrównoważone obciążeniem dla innych procesów roboczych, ponieważ środowisko uruchomieniowe Durable Functions będzie już załadować je do pamięci. Staje się to szczególnie problematyczne, jeśli funkcje działania są długotrwałe.

Jeśli używane środowisko uruchomieniowe języka powoduje ograniczenie współbieżności, należy zaktualizować ustawienia współbieżności rozszerzenia Durable Functions, aby dopasować ustawienia współbieżności środowiska uruchomieniowego języka. Gwarantuje to, że środowisko uruchomieniowe Durable Functions nie będzie próbowało jednocześnie uruchamiać większej liczby funkcji niż jest dozwolone przez środowisko uruchomieniowe języka, co pozwala na równoważenie obciążenia wszystkich oczekujących działań na innych maszynach wirtualnych. Jeśli na przykład masz aplikację w języku Python, która ogranicza współbieżność do 4 funkcji (być może jest skonfigurowana tylko z 4 wątkami w procesie roboczym pojedynczego języka lub 1 wątek w procesach roboczych języka 4), należy skonfigurować zarówno maxConcurrentOrchestratorFunctions , jak i maxConcurrentActivityFunctions do 4.

Aby uzyskać więcej informacji i zaleceń dotyczących wydajności języka Python, zobacz Zwiększanie wydajności przepływności aplikacji języka Python w usłudze Azure Functions. Techniki wymienione w tej dokumentacji referencyjnej dla deweloperów języka Python mogą mieć znaczący wpływ na wydajność i skalowalność rozszerzenia Durable Functions.

Liczba partycji

Niektórzy dostawcy magazynu używają mechanizmu partycjonowania i umożliwiają określenie parametru partitionCount .

W przypadku korzystania z partycjonowania pracownicy nie konkurują bezpośrednio o poszczególne elementy robocze. Zamiast tego elementy robocze są najpierw pogrupowane w partitionCount partycje. Te partycje są następnie przypisywane do procesów roboczych. Takie podzielone na partycje podejście do dystrybucji obciążenia może pomóc zmniejszyć łączną liczbę wymaganych dostępu do magazynu. Ponadto może włączyć buforowanie wystąpień i poprawić lokalność, ponieważ tworzy koligację: wszystkie elementy robocze dla tego samego wystąpienia są przetwarzane przez ten sam proces roboczy.

Uwaga

Limity partycjonowania są skalowane w poziomie, ponieważ większość partitionCount procesów roboczych może przetwarzać elementy robocze z partycjonowanej kolejki.

W poniższej tabeli przedstawiono dla każdego dostawcy magazynu, który kolejki są partycjonowane, oraz dozwolony zakres i wartości domyślne parametru partitionCount .

Dostawca usługi Azure Storage Dostawca magazynu netherite Dostawca magazynu MSSQL
Komunikaty wystąpień Partitioned Partitioned Nie partycjonowane
Komunikaty o aktywności Nie partycjonowane Partitioned Nie partycjonowane
Domyślny partitionCount 4 12 nie dotyczy
Maksymalna partitionCount 16 32 nie dotyczy
Dokumentacja Zobacz Skalowanie w poziomie programu Orchestrator Zobacz Zagadnienia dotyczące liczby partycji nie dotyczy

Ostrzeżenie

Nie można już zmienić liczby partycji po utworzeniu centrum zadań. W związku z tym zaleca się ustawienie jej na wystarczająco dużą wartość, aby uwzględnić przyszłe wymagania dotyczące skalowania w poziomie dla wystąpienia centrum zadań.

Konfiguracja liczby partycji

Parametr partitionCount można określić w pliku host.json . Poniższy przykład host.json fragment kodu ustawia durableTask/storageProvider/partitionCount właściwość (lub durableTask/partitionCount w rozszerzeniu Durable Functions 1.x) na 3wartość .

Durable Functions 2.x

{
  "extensions": {
    "durableTask": {
      "storageProvider": {
        "partitionCount": 3
      }
    }
  }
}

Durable Functions 1.x

{
  "extensions": {
    "durableTask": {
      "partitionCount": 3
    }
  }
}

Zagadnienia dotyczące minimalizowania opóźnień wywołań

W normalnych okolicznościach żądania wywołania (do działań, orkiestratorów, jednostek itp.) powinny być przetwarzane dość szybko. Nie ma jednak gwarancji na maksymalne opóźnienie dowolnego żądania wywołania, ponieważ zależy od czynników, takich jak: typ zachowania skalowania planu usługi App Service, ustawień współbieżności i rozmiaru listy prac aplikacji. W związku z tym zalecamy inwestowanie w testy obciążeniowe w celu mierzenia i optymalizowania opóźnień końcowych aplikacji.

Cele dotyczące wydajności

Podczas planowania używania rozszerzenia Durable Functions dla aplikacji produkcyjnej należy wziąć pod uwagę wymagania dotyczące wydajności na wczesnym etapie procesu planowania. Oto niektóre podstawowe scenariusze użycia:

  • Wykonywanie działań sekwencyjnych: w tym scenariuszu opisano funkcję orkiestratora, która uruchamia szereg funkcji działań po drugiej. Najbardziej przypomina przykład tworzenia łańcuchów funkcji.
  • Równoległe wykonywanie działań: w tym scenariuszu opisano funkcję orkiestratora, która wykonuje wiele funkcji działań równolegle przy użyciu wzorca Fan-out, Fan-in .
  • Przetwarzanie równoległe odpowiedzi: ten scenariusz jest drugą połowę wzorca Fan-out, Fan-in . Koncentruje się na wydajności wentylatora. Należy pamiętać, że w przeciwieństwie do fan-out, fan-in jest wykonywany przez pojedyncze wystąpienie funkcji orkiestratora i dlatego może działać tylko na jednej maszynie wirtualnej.
  • Przetwarzanie zdarzeń zewnętrznych: ten scenariusz reprezentuje pojedyncze wystąpienie funkcji orkiestratora, które czeka na zdarzenia zewnętrzne, pojedynczo.
  • Przetwarzanie operacji jednostki: w tym scenariuszu sprawdza, jak szybko pojedyncza jednostka Counter może przetwarzać stały strumień operacji.

Udostępniamy numery przepływności dla tych scenariuszy w odpowiedniej dokumentacji dla dostawców magazynu. W szczególności:

Napiwek

W przeciwieństwie do wentylatora operacje wentylatora są ograniczone do pojedynczej maszyny wirtualnej. Jeśli aplikacja korzysta ze wzorca fan-out, fan-in i martwisz się o wydajność wentylatora, rozważ podzielenie wentylatora funkcji działania na wiele podarancji.

Następne kroki