Indy Servers - Thread Model (1 / 3)

· Fonctionnalités

Les serveurs Indy utilisent des threads pour gérer les connexions clientes : chaque fois qu'un nouveau client se connecte au serveur, un nouveau thread est créé et c'est ce thread qui gère la connexion. Si tu as 100 connexions, il y aura 100 threads. De plus, Indy utilise des sockets bloquantes, ce qui signifie que lorsque tu lis ou écris, la fonction ne retourne pas tant qu'elle n'est pas terminée.

Ce modèle a quelques avantages : il est facile à coder, car le code est traité de façon séquentielle. Mais en inconvénient, si le nombre de connexions augmente, les performances se dégradent à cause du thread context switch. Le context switching consiste à stocker l'état d'un thread pour pouvoir le restaurer plus tard et reprendre l'exécution. Un context switching rapide entre threads est coûteux en termes d'utilisation CPU. Si tu crées 1000 connexions sur un serveur, tu verras que le CPU travaille bien qu'aucune donnée ne soit échangée : cette utilisation CPU est due au thread context switch.

Alternatives au modèle Indy 

Au lieu d'utiliser 1 thread par connexion, il existe des alternatives comme IOCP (sur Windows) ou EPOLL (sur Linux) qui utilisent un pool de threads pour gérer les connexions et utilisent des sockets non bloquantes. Ce modèle est beaucoup plus efficace lorsque le nombre de connexions simultanées est élevé et se met bien mieux à l'échelle que le modèle de threads Indy par défaut.

IOCP (Windows) 

Les ports de complétion d'E/S fournissent un modèle de threads efficace pour traiter de multiples requêtes d'E/S asynchrones sur un système multiprocesseur. Quand un processus crée un port de complétion d'E/S, le système crée un objet de file associé pour les threads dont le seul but est de servir ces requêtes. Les processus qui gèrent de nombreuses requêtes d'E/S asynchrones concurrentes peuvent le faire plus rapidement et plus efficacement en utilisant des ports de complétion d'E/S conjointement à un pool de threads pré-alloué, plutôt qu'en créant des threads au moment où ils reçoivent une requête d'E/S.

Le modèle IOCP utilise des sockets non bloquantes : au lieu d'utiliser Select, il utilise la fonction AcceptEx et un pool de threads pour gérer les connexions clientes.

Pour activer IOCP sur un serveur Indy sgcWebSockets, regarde le code ci-dessous.

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

Un nouveau serveur WebSocket + HTTP sera créé et un pool de threads gérera les connexions ; le nombre de threads utilisés dépendra du nombre de CPU sur lequel le serveur tourne.

EPOLL (Linux) 

Epoll est un appel système du noyau Linux pour un mécanisme évolutif de notification d'événements d'E/S, introduit pour la première fois dans la version 2.5.44 du noyau Linux. Sa fonction est de surveiller plusieurs descripteurs de fichier pour voir si une E/S est possible sur l'un d'eux. Il est destiné à remplacer les anciens appels système POSIX select et poll, pour obtenir de meilleures performances dans les applications plus exigeantes, où le nombre de descripteurs surveillés est important.

Regarde le tableau suivant comparant les performances pour 100 000 opérations de surveillance :

Nb. opérations poll select epoll
10 0.61 0.73 0.41
1002.93.00.42
100035350.53
100009909300.66

Utiliser epoll est donc bien plus rapide dès que tu as plus d'une dizaine de descripteurs de fichier à surveiller.

Le modèle EPOLL utilise des sockets non bloquantes : au lieu d'utiliser Select, il utilise la fonction Accept async et un pool de threads pour gérer les connexions clientes.

Pour activer EPOLL sur un serveur Indy sgcWebSockets, regarde le code ci-dessous.

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