Udostępnij przez


Skalowalność

Termin skalowalności jest często niewłaściwy. W tej sekcji podano podwójną definicję:

  • Skalowalność to możliwość pełnego wykorzystania dostępnej mocy obliczeniowej w systemie wieloprocesorowym (2, 4, 8, 32 lub więcej procesorów).
  • Skalowalność to możliwość obsługi dużej liczby klientów.

Te dwie powiązane definicje są często określane jako skalowanie w górę. Na końcu tego tematu znajdują się porady dotyczące skalowania w.

Ta dyskusja koncentruje się wyłącznie na pisaniu skalowalnych serwerów, a nie skalowalnych klientów, ponieważ skalowalne serwery są bardziej typowymi wymaganiami. Ta sekcja dotyczy również skalowalności tylko w kontekście serwerów RPC i RPC. Najlepsze rozwiązania dotyczące skalowalności, takie jak zmniejszenie rywalizacji, unikanie częstych chybień pamięci podręcznej w lokalizacjach pamięci globalnej lub unikanie udostępniania fałszywych informacji, nie są tu omawiane.

Model wątkowania RPC

Gdy wywołanie RPC jest odbierane przez serwer, procedury serwera (procedury menedżera) są wywoływane w wątku dostarczonym przez RPC. RPC używa adaptacyjnej puli wątków, która zwiększa się i zmniejsza w miarę wahań obciążenia. Począwszy od systemu Windows 2000, rdzeniem puli wątków RPC jest port ukończenia. Port ukończenia i jego użycie przez RPC są dostrojone do zera do procedur serwera rywalizacji. Oznacza to, że pula wątków RPC agresywnie zwiększa liczbę wątków obsługi, jeśli niektóre staną się zablokowane. Działa on na domniemanie, że blokowanie jest rzadkie, a jeśli wątek zostanie zablokowany, jest to tymczasowy warunek, który jest szybko rozwiązany. Takie podejście zapewnia wydajność dla serwerów o niskiej rywalizacji. Na przykład wywołanie void wywołania serwera RPC działającego na serwerze 550MHz z ośmioprocesorowym dostępem przez sieć SAN (high speed system area network) obsługuje ponad 30 000 wywołań void na sekundę z ponad 200 klientów zdalnych. Reprezentuje to ponad 108 milionów wywołań na godzinę.

Wynikiem jest to, że agresywna pula wątków rzeczywiście dostaje się w sposób, gdy rywalizacja na serwerze jest wysoka. Aby zilustrować, wyobraź sobie serwer o dużym obciążeniu używany do zdalnego uzyskiwania dostępu do plików. Załóżmy, że serwer przyjmuje najprostsze podejście: po prostu odczytuje/zapisuje plik synchronicznie w wątku, w którym RPC wywołuje procedurę serwera. Załóżmy również, że mamy serwer z czterema procesorami obsługującymi wielu klientów.

Serwer rozpocznie się od pięciu wątków (faktycznie różni się to, ale pięć wątków jest używanych dla uproszczenia). Po odebraniu pierwszego wywołania RPC, wysyła wywołanie do procedury serwera, a procedury serwera występują problemy z we/wy. Rzadko pomija pamięć podręczną plików, a następnie blokuje oczekiwanie na wynik. Gdy tylko blokuje, piąty wątek jest zwalniany w celu odebrania żądania, a szósty wątek jest tworzony jako rezerwa gorąca. Zakładając, że każda dziesiąta operacja we/wy pomija pamięć podręczną i zablokuje 100 milisekund (dowolną wartość czasu) i zakładając, że serwer czteroprocesorowy obsługuje około 20 000 wywołań na sekundę (5000 wywołań na procesor), uproszczone modelowanie przewiduje, że każdy procesor będzie powodować około 50 wątków. Zakłada się, że wywołanie, które będzie blokowane co 2 milisekundy, a po 100 milisekundach pierwszy wątek zostanie uwolniony ponownie, więc pula będzie ustabilizować się na około 200 wątków (50 na procesor).

Rzeczywiste zachowanie jest bardziej skomplikowane, ponieważ duża liczba wątków spowoduje dodatkowe przełączniki kontekstowe, które spowalniają serwer, a także spowalniają szybkość tworzenia nowych wątków, ale podstawowa idea jest jasna. Liczba wątków szybko rośnie, gdy wątki na serwerze zaczynają blokować się i czekać na coś (czy to we/wy, lub dostęp do zasobu).

RPC i port ukończenia, który bramuje żądania przychodzące, spróbuje zachować liczbę użytecznych wątków RPC na serwerze, które mają być równe liczbie procesorów na maszynie. Oznacza to, że na serwerze z czterema procesorami po powrocie wątku do RPC, jeśli istnieją co najmniej cztery wątki RPC, piąty wątek nie może odebrać nowego żądania, a zamiast tego będzie siedzieć w stanie rezerwy gorącej w przypadku jednego z aktualnie używanych wątków bloków. Jeśli piąty wątek czeka wystarczająco długo, ponieważ rezerwa gorąca bez liczby użytecznych wątków RPC spada poniżej liczby procesorów, zostanie ona zwolniona, czyli pula wątków zmniejszy się.

Wyobraź sobie serwer z wieloma wątkami. Jak wyjaśniono wcześniej, serwer RPC kończy się wieloma wątkami, ale tylko wtedy, gdy wątki często blokują. Na serwerze, na którym wątki często blokują, wątek, który powraca do RPC, jest wkrótce wyjęty z gorącej listy rezerwowej, ponieważ wszystkie aktualnie użyteczne wątki bloku i otrzymuje żądanie przetworzenia. Gdy wątek blokuje, dyspozytor wątku w jądrze przełącza kontekst do innego wątku. Ten przełącznik kontekstu sam w sobie zużywa cykle procesora CPU. Następny wątek będzie wykonywać inny kod, uzyskiwać dostęp do różnych struktur danych i będzie miał inny stos, co oznacza, że szybkość trafień pamięci podręcznej (pamięci podręczne L1 i L2) będzie znacznie niższa, co spowoduje wolniejsze wykonywanie. Wiele wątków wykonywanych jednocześnie zwiększa rywalizację o istniejące zasoby, takie jak sterta, sekcje krytyczne w kodzie serwera itd. Dodatkowo zwiększa to rywalizację o konwoje na formularzu zasobów. Jeśli pamięć jest niska, wykorzystanie pamięci wywierane przez dużą i rosnącą liczbę wątków spowoduje błędy stron, co dodatkowo zwiększa szybkość blokowania wątków i powoduje utworzenie jeszcze większej liczby wątków. W zależności od tego, jak często blokuje i ile pamięci fizycznej jest dostępna, serwer może ustabilizować się na pewnym niższym poziomie wydajności z wysoką szybkością przełączania kontekstu lub może pogorszyć się do punktu, w którym jest tylko wielokrotnie uzyskiwany dostęp do dysku twardego i przełączania kontekstu bez wykonywania żadnej rzeczywistej pracy. Ta sytuacja nie będzie oczywiście widoczna pod lekkim obciążeniem, ale duże obciążenie szybko przenosi problem na powierzchnię.

Jak można temu zapobiec? Jeśli wątki mają być blokowane, deklaruj wywołania jako asynchroniczne, a gdy żądanie wejdzie w procedurę serwera, przeprowadź kolejkę do puli wątków roboczych, które korzystają z asynchronicznych możliwości systemu we/wy i/lub RPC. Jeśli serwer z kolei wykonuje wywołania RPC, wykonaj te asynchroniczne i upewnij się, że kolejka nie rośnie zbyt duży. Jeśli procedury serwera wykonują operacje we/wy pliku, użyj asynchronicznego we/wy pliku, aby kolejkować wiele żądań do systemu we/wy i mieć tylko kilka wątków do kolejki i pobierać wyniki. Jeśli procedury serwera wykonują operacje we/wy sieci, ponownie użyj asynchronicznych możliwości systemu, aby wysyłać żądania i pobierać odpowiedzi asynchronicznie i używać jak najmniejszej liczby wątków, jak to możliwe. Po zakończeniu operacji we/wy lub wywołaniu wywołania RPC asynchronicznego, które dostarczyło żądanie. Umożliwi to uruchamianie serwera z jak najmniejszą liczbą wątków, co zwiększa wydajność i liczbę klientów, których serwer może obsługiwać.

Skalowanie w poziomie

RPC można skonfigurować do pracy z równoważeniem obciążenia sieciowego (NLB), jeśli równoważenie obciążenia sieciowego jest skonfigurowane tak, aby wszystkie żądania z danego adresu klienta przechodziły do tego samego serwera. Ponieważ każdy klient RPC otwiera pulę połączeń (aby uzyskać więcej informacji, zobacz RPC i Network), ważne jest, aby wszystkie połączenia z puli danego klienta kończyły się na tym samym komputerze serwera. Tak długo, jak ten warunek jest spełniony, klaster równoważenia obciążenia sieciowego można skonfigurować tak, aby działał jako jeden duży serwer RPC z potencjalnie doskonałą skalowalnością.