Serwery Indy – model wątków (1/3)

· Funkcje

Serwery Indy używają wątków do obsługi połączeń klientów. Za każdym razem, gdy nowy klient łączy się z serwerem, tworzony jest nowy wątek obsługujący to połączenie — przy 100 połączeniach działa 100 wątków. Indy używa też blokujących gniazd, co oznacza, że funkcja odczytu lub zapisu nie powraca, dopóki operacja nie zostanie ukończona.

Ten model ma pewne zalety — kod jest łatwy do pisania, ponieważ wykonywany jest sekwencyjnie. Wadą jest jednak to, że wraz ze wzrostem liczby połączeń wydajność coraz bardziej spada z powodu przełączania kontekstu wątków. Przełączanie kontekstu polega na zapisaniu stanu wątku, by móc wznowić jego wykonanie w późniejszym czasie. Częste przełączanie kontekstu jest kosztowne pod względem wykorzystania procesora. Tworząc 1000 połączeń na serwerze, można zaobserwować obciążenie procesora nawet bez wymiany danych — właśnie z powodu przełączania kontekstu wątków.

Alternatywy dla modelu Indy

Zamiast używać jednego wątku na każde połączenie, można skorzystać z alternatyw, takich jak IOCP (dla Windows) lub EPOLL (dla Linux), które korzystają z puli wątków do obsługi połączeń i używają nieblokujących gniazd. Ten model jest znacznie wydajniejszy przy dużej liczbie jednoczesnych połączeń i skaluje się o wiele lepiej niż domyślny model wątkowy Indy.

IOCP (Windows)

Porty zakończenia we/wy zapewniają wydajny model wątkowy do przetwarzania wielu asynchronicznych żądań we/wy na systemie wieloprocesorowym. Gdy proces tworzy port zakończenia we/wy, system tworzy powiązany obiekt kolejki dla wątków, których jedynym zadaniem jest obsługa tych żądań. Procesy obsługujące wiele równoczesnych asynchronicznych żądań we/wy mogą działać szybciej i wydajniej, używając portów zakończenia we/wy w połączeniu z wcześniej przydzieloną pulą wątków, niż tworząc wątki w momencie otrzymania żądania.

Model IOCP używa nieblokujących gniazd — zamiast Select korzysta z funkcji AcceptEx oraz puli wątków do obsługi połączeń klientów.

Aby włączyć IOCP na serwerze Indy sgcWebSockets, sprawdź poniższy kod:

oServer := TsgcWebSocketHTTPServer.Create(nil);
oServer.NotifyEvents := neNOSync;
oServer.IOHandlerOptions.IOHandlerType := iohIOCP;
oServer.Active := True; 

Zostanie utworzony nowy serwer WebSocket + HTTP, a pula wątków będzie obsługiwać połączenia — liczba używanych wątków zależy od liczby rdzeni procesora, na którym działa serwer.

EPOLL (Linux)

Epoll to wywołanie systemowe jądra Linux zapewniające skalowalny mechanizm powiadamiania o zdarzeniach we/wy, wprowadzone po raz pierwszy w wersji 2.5.44 jądra Linux. Jego funkcją jest monitorowanie wielu deskryptorów plików, aby sprawdzić, czy na którymś z nich jest możliwe wykonanie operacji we/wy. Ma zastąpić starsze wywołania systemowe POSIX select i poll, zapewniając lepszą wydajność w bardziej wymagających aplikacjach z dużą liczbą monitorowanych deskryptorów.

Poniższa tabela porównuje wydajność dla 100 000 operacji monitorowania:

Liczba operacji poll select epoll
10 0.61 0.73 0.41
1002.93.00.42
100035350.53
100009909300.66

Jak widać, epoll jest znacznie szybszy, gdy masz więcej niż około 10 deskryptorów plików do monitorowania.

Model EPOLL używa nieblokujących gniazd — zamiast Select korzysta z asynchronicznej funkcji Accept oraz puli wątków do obsługi połączeń klientów.

Aby włączyć EPOLL na serwerze Indy sgcWebSockets, sprawdź poniższy kod:

oServer := TsgcWebSocketHTTPServer.Create(nil);
oServer.NotifyEvents := neNOSync;
oServer.IOHandlerOptions.IOHandlerType := iohEPOLL;
oServer.Active := True;