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 |
| 100 | 2.9 | 3.0 | 0.42 |
| 1000 | 35 | 35 | 0.53 |
| 10000 | 990 | 930 | 0.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;
