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ę:
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
- Pobierz historię aranżacji.
- 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
iextendedSessionIdleTimeoutInSeconds
. 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
InstanceCacheSizeMB
usługę , co najmniej ostatnio używane dane wystąpienia zostanie wykluczone. JeśliCacheOrchestrationCursors
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 |
Maksimum 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 3
wartość .
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:
- w przypadku dostawcy usługi Azure Storage zobacz Cele wydajności.
- w przypadku dostawcy magazynu Netherite zobacz Podstawowe scenariusze.
- dla dostawcy magazynu MSSQL zobacz Orchestration Throughput Benchmarks (Testy porównawcze przepływności orkiestracji).
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.