Indy-Server verwenden Threads, um Client-Verbindungen zu verarbeiten. Jedes Mal, wenn sich ein neuer Client verbindet, wird ein neuer Thread erstellt, der die Verbindung übernimmt. Bei 100 Verbindungen sind das also 100 Threads. Zusätzlich nutzt Indy blockierende Sockets, das heißt: Wenn du liest oder schreibst, kehrt die Funktion erst zurück, sobald der Vorgang abgeschlossen ist.
Dieses Modell hat Vorteile, etwa einfache Programmierung, weil der Code sequenziell läuft. Nachteilig ist: Steigt die Anzahl der Verbindungen, sinkt die Leistung wegen des Thread-Context-Switching. Beim Context-Switching wird der Zustand eines Threads gespeichert, damit er später wiederhergestellt werden kann. Schnelles Context-Switching zwischen vielen Threads kostet CPU. Wenn du 1000 Verbindungen zu einem Server aufbaust, läuft die CPU, obwohl gar keine Daten ausgetauscht werden — diese CPU-Last entsteht durch das Context-Switching.
Alternativen zum Indy-Modell
Statt einen Thread pro Verbindung zu verwenden, gibt es Alternativen wie IOCP (für Windows) oder EPOLL (für Linux), die einen Thread-Pool für die Verbindungen nutzen und auf nicht-blockierende Sockets setzen. Dieses Modell ist bei vielen gleichzeitigen Verbindungen deutlich effizienter und skaliert wesentlich besser als das Standard-Indy-Thread-Modell.
IOCP (Windows)
I/O Completion Ports bieten ein effizientes Threading-Modell zur Verarbeitung mehrerer asynchroner I/O-Anfragen auf einem Multiprozessorsystem. Wenn ein Prozess einen I/O Completion Port erstellt, legt das System ein zugehöriges Queue-Objekt für Threads an, deren einziger Zweck es ist, diese Anfragen zu bedienen. Prozesse, die viele gleichzeitige asynchrone I/O-Anfragen verarbeiten, kommen mit I/O Completion Ports und einem vorgeladenen Thread-Pool schneller und effizienter zurecht, als wenn sie pro I/O-Anfrage einen neuen Thread erstellen.
Das IOCP-Modell verwendet nicht-blockierende Sockets. Statt Select nutzt es die AcceptEx-Funktion und einen Thread-Pool zur Verarbeitung der Client-Verbindungen.
So aktivierst du IOCP für den sgcWebSockets-Indy-Server:
oServer := TsgcWebSocketHTTPServer.Create(nil); oServer.NotifyEvents := neNOSync; oServer.IOHandlerOptions.IOHandlerType := iohIOCP; oServer.Active := True;
Es wird ein neuer WebSocket- + HTTP-Server erstellt, und ein Thread-Pool verarbeitet die Verbindungen. Die Anzahl der verwendeten Threads richtet sich nach der Anzahl der CPUs, auf denen der Server läuft.
EPOLL (Linux)
Epoll ist ein Linux-Kernel-Systemaufruf für einen skalierbaren I/O-Event-Notifizierungsmechanismus, erstmals eingeführt in Version 2.5.44 des Linux-Kernels. Seine Aufgabe ist es, mehrere File-Deskriptoren zu überwachen, um zu prüfen, ob für einen davon I/O möglich ist. Es ist als Ersatz für die älteren POSIX-Systemaufrufe select und poll gedacht und soll in anspruchsvolleren Anwendungen mit vielen überwachten File-Deskriptoren bessere Leistung liefern.
Sieh dir die folgende Tabelle an — sie vergleicht die Leistung für 100.000 Überwachungsoperationen:
| Anz. Operationen | 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 |
Sobald du mehr als rund 10 File-Deskriptoren überwachst, ist epoll also deutlich schneller.
Das EPOLL-Modell verwendet nicht-blockierende Sockets. Statt Select nutzt es die asynchrone Accept-Funktion und einen Thread-Pool zur Verarbeitung der Client-Verbindungen.
So aktivierst du EPOLL für den sgcWebSockets-Indy-Server:
oServer := TsgcWebSocketHTTPServer.Create(nil); oServer.NotifyEvents := neNOSync; oServer.IOHandlerOptions.IOHandlerType := iohEPOLL; oServer.Active := True;
