Magazine Focus Emploi

Sockets Unix versus sockets réseau

Publié le 05 février 2019 par Abouchard

Contexte

Quand on installe une infrastructure web, on commence souvent par utiliser un unique serveur, qui fait tourner à la fois la partie applicative (serveur HTTP + code applicatif) et les bases de données.

Par la suite, on sépare habituellement ces deux parties sur des serveurs séparés.

Sockets Unix versus sockets réseau

Généralement, on procède de la sorte pour des raisons de performances. Chaque machine est dédiée à une tâche, avec des ressources dédiées. Il est facile de faire évoluer l’un ou l’autre séparément, en fonction des besoins.
Incidemment, cela permet aussi − toujours pour améliorer les performances − de multiplier les serveurs frontaux.

Sockets Unix versus sockets réseau

Ce n’est efficace que si l’application est gourmande en calculs ; la base de données peut rapidement devenir un goulet d’étranglement. Il est possible de contourner cela en ajoutant des serveurs de cache, ou de répliquer la base de données pour ajouter des nœuds en lecture.

Sockets Unix versus sockets réseau

Performances

Ce qu’on appelle «performances» revêt plusieurs aspects. La plupart du temps, on imagine qu’une plate-forme performante est une plate-forme qui est capable de supporter un très grand nombre d’utilisateurs simultanés. Et c’est effectivement une définition pleine de sens.

Mais pour qu’une plate-forme se retrouve à devoir gérer un très grand nombre de connexions en même temps, il faut qu’elle ait commencé par offrir un service de très haute qualité, sans quoi les utilisateurs ne seront pas au rendez-vous. On touche alors du doigt une autre facette des performances : proposer un service qui réponde le plus vite aux sollicitations des utilisateurs. Qui soit “snappy”, comme disent les anglophones.
Et pour cela, il faut que chaque requête obtienne une réponse la plus rapide possible.

On peut penser que les techniques qui permettent de soutenir un grand nombre de connexions simultanées sont aussi celles qui permettent de répondre au plus vite à chaque requête individuelle. Cela paraît logique ; si on répond très vite à une requête, on sera capable de prendre plus de requêtes en charge. Sauf que ça ne fonctionne pas comme ça.
Les connexions réseau (entre les serveurs frontaux et les serveurs de bases de données) ont un coût : elles ajoutent de la latence. La connexion de l’un à l’autre prend du temps ; faire transiter les requêtes de l’un vers l’autre, puis les réponses en sens inverse, prend du temps.

Des études montrent que l’être humain perçoit les latences à partir de 13 ms, et à partir de 75 ms on peut considérer que l’expérience utilisateur est dégradée.
Il est donc important de réduire toutes les sources de latence qui s’additionnent les unes aux autres. Évidemment, le plus gros de ces sources reste au niveau de la base de données, du traitement applicatif et de tous les éléments graphiques chargés par le navigateur ; il faut les traiter en priorité, car il ne sert à rien de vouloir économiser des poignées de millisecondes si on perd des secondes pleines sur une requête SQL inefficace ou sur des chargements d’images non optimisées.

Test

Pour réduire les latences réseau, on peut simplement tout regrouper sur une même machine. Mais le gain est vraiment tangible si on utilise des sockets Unix au lieu des sockets réseau habituelles. Ainsi, les données transitent directement à travers le kernel au lieu de traverser toutes les couches de gestion du réseau.

Pour tester l’effet, j’ai fait un petit comparatif. J’ai pris une application PHP utilisant de manière intensive deux bases de données (une Redis et une MySQL). D’un côté, je l’ai déployée sur une architecture avec deux machines virtuelles (dual CPU 3,1 GHz, 7 GO de RAM), et de l’autre côté sur une architecture avec une seule machine (dual CPU 2,3 GHz).
Sur l’architecture à deux machines, l’application se connecte évidemment par le réseau pour accéder aux données. Sur l’architecture à une seule machine, tout passe par des sockets Unix.

Pour faire simple, et même si ce test n’est pas hyper pertinent (parce que pas très rigoureux), je suis arrivé à un temps de génération brut de la page presque divisé par deux : une moyenne de 215 ms contre 410 ms.
Sur une page qui utilise moins la base de données, la différence est moins grande mais néanmoins palpable : 56 ms contre 84 ms en moyenne.

Cela est corroboré par plusieurs tests trouvés sur le web, qui indiquent que la latence des sockets Unix est 25% à 33% plus faible que celle des sockets réseau, alors que leur bande passante peut être jusqu’à deux fois plus grande (cf. ici, ici, ici, et ici).

Configuration

La configuration pour utiliser les sockets Unix est très simple.

Sous Ubuntu, MySQL est déjà configuré par défaut pour ouvrir une socket Unix en plus d’écouter sur un port réseau. Vérifiez que vous avez les lignes suivantes (ou similaires) dans le fichier /etc/mysql/mysql.conf.d/mysqld.cnf :

[mysqld_safe]
socket = /var/run/mysqld/mysqld.sock

Pour la connexion client, tout dépend du langage de programmation et de la librairie utilisée pour vous connecter au serveur. Mais la plupart du temps, il suffit de fournir “localhost” comme nom de serveur (et pas “127.0.0.1”) et le chemin vers la socket Unix dans un paramètre supplémentaire.

Pour Redis, éditez le fichier /etc/redis/redis.conf pour vérifier que les lignes suivantes (ou similaires) ne sont pas commentées :

unixsocket /var/run/redis/redis-server.sock
unixsocketperm 766

Là encore, la connexion dépend du langage et de l’objet de connexion. Mais il suffit habituellement de fournir le chemin vers la socket Unix à la place du nom de serveur.

Pour Memcache, les choses sont légèrement différentes. Au contraire de MySQL et Redis, il n’est pas capable d’écouter en même temps sur une socket Unix et une socket réseau. Il faut donc faire un choix (qui n’est pas compliqué dans notre cas, mais il faut l’avoir en tête quand on met en place une architecture serveur).
Pour que Memcache écoute sur une socket Unix, éditez le fichier /etc/memcached.conf pour ajouter les lignes suivantes :

-s /var/run/memcached/memcached.sock
-a 0766

Conclusion

De nos jours, nous avons à notre disposition des technologies qui facilitent grandement la scalabilité verticale (faire grossir le serveur quand les besoins augmentent), alors qu’il y a encore 10 ans on était obligé de passer par de la scalabilité horizontale (ajouter des serveurs supplémentaires), qui est plus délicate à mettre en œuvre − surtout si elle n’est pas prise en compte dès la conception.

Donc pour une application web qui doit faire ses preuves, pour laquelle on a tout intérêt à privilégier le confort des premiers utilisateurs plutôt que d’espérer devoir gérer une forte charge, je ne peux que vous conseiller de tout faire tenir sur un seul serveur, et de faire grossir ce serveur si vos besoins augmentent.
Au passage, on peut remarquer que d’un point de vue financier, c’est une stratégie qui tient complètement la route.

En fait, il ne s’agit que d’un exemple de l’adage disant qu’il ne faut pas optimiser prématurément.


Retour à La Une de Logo Paperblog

A propos de l’auteur


Abouchard 392 partages Voir son profil
Voir son blog

l'auteur n'a pas encore renseigné son compte l'auteur n'a pas encore renseigné son compte