Netty (6/6) : Constatations ; conclusion

262 views
Skip to first unread message

Laurent Caillette

unread,
Dec 23, 2016, 5:16:00 AM12/23/16
to tec...@googlegroups.com
Allez, pour fêter Noël, deux épisode dans la même journée !

Après une présentation de l'existant et des produits utilisés, voici
ce qu'on peut dire du passage à Netty.


=== Constatations

Avec l'architecture LMAX comme source d'inspiration, le code
applicatif a été conçu dès le départ pour être non-bloquant. On peut
considérer le choix de Jetty et CometD comme des accidents de parcours
avant le mariage d'amour avec Netty. Tout n'est pas simple pour
autant, et là on parle d'une migration, pas d'un développement parti
de rien.

Jetty et CometD offrent de nombreuses fonctionnalités qu'on apprécie
seulement quand elles ne sont plus là. Parmi les plus faciles à
évoquer, mentionnons la reconnexion automatique du client CometD ainsi
que l'interface programmatique cohérente pour les pages HTML avec les
Servlets.

Faut-il s'autoriser la réécriture de pareilles choses avec ses mains ?
Faut-il s'engager vers des produits comme Ratpack ou `Vert.x` ? Ce
n'est pas parce que ces produits-là font plein de choses qu'ils font
celles dont on a besoin.

Parmi les parties un peu velues de l'application en contact avec la
partie réseau, on trouve de la sérialisation maison, et une
authentification à deux facteurs assez complexe. Quand on est devant
il est clair que ça rentre sans problème dans le modèle de
programmation de Netty, alors que Ratpack et `Vert.x` n'offriraient
pas d'aide là où il y a vraiment besoin. Jusqu'au jour où on se
retrouve à coder un dispositif d'aiguillage des requêtes HTTP pour
certaines fonctionnalités annexes, et là on se demande si on n'est pas
en train de réécrire Ratpack. Sauf que Ratpack n'offre presque rien
pour les WebSockets, qui constituent le fonds de commerce de
l'application migrée.

Netty permet d'intervenir à tous les endroits où ça a un sens, avec
des fois de belles simplifications. Par exemple, on peut facilement
enrichir les en-têtes HTTP lors de la promotion d'une connexion en
WebSocket pour indiquer la version du serveur, ou effectuer des
traitements propres à la session d'un utilisateur sans se poser de
questions sur la concurrence d'accès si on se trouve dans un
``ChannelHandler``. Que ce soit pour des tests ou pour du code
applicatif, Netty a toujours permis de placer la bonne bidouille au
bon endroit, sans que ça soit plus compliqué que nécessaire.

Netty a également permis le ménage dans les poules de fils d'exécution
("thread pools"). Alors que Jetty en crée préalablement des centaines,
avec Netty on a juste le nécessaire (2 par cœur de processeur en
général) et les traces de fils d'exécution ("thread dumps") ne font
plus 10 mètres de haut. Les clients Netty du robot de test de charge
ont également bénéficié du partage de la même boucle d'événements.

Donc le bilan de cette migration en particulier, c'est que oui grâce à
Netty on gagne à réécrire son propre conteneur d'application, parce
qu'au moins on peut ne mettre dedans que ce qu'on a besoin, au lieu de
pleurer devant la très obscure boîte Jetty-CometD dès qu'il y a
quelque chose qui a l'air de ne plus marcher. Je ne dis pas que tout
le monde doit en faire autant, je constate juste qu'aujourd'hui les
conteneurs applicatifs du marché (J2EE notamment) orientent vers des
modèles de programmation qui n'aident pas à se sentir intelligent.

Après pour enfoncer le clou dans le registre "Netty rulez" ("Netty
über alles"), mettre du Netty partout ça veut dire aussi limiter la
prolifération des bibliothèques techniques pour tripoter le réseau.

Pour ce qui est des performances, avant la migration, l'application se
traînait à `650 messages/seconde` en entrée environ pour 99
utilisateurs connectés. Chaque utilisateur envoie une mise à jour vue
par tous les autres, ce qui faisait environ `64.000 messages/seconde`
en sortie.

Le profileur a montré que c'était la sérialisation JSON imposée par
CometD qui consommait le plus de ressources. Netty a permis d'utiliser
une sérialisation maison. Maintenant avec 99 utilisateurs connectés,
l'application consomme `2400 messages/seconde` pour un débit total de
`240.000 messages/seconde`. Le profileur montre que le facteur
limitant ce n'est plus la plomberie réseau mais le code applicatif qui
s'exécute séquentiellement. Comme c'est presque deux fois et demi le
niveau de performance espéré, il n'y a pas eu d'autre effort
d'optimisation.

Un dernier mot sur les performances : dans un "banc d'essai"
https://www.techempower.com/benchmarks/#section=data-r13&hw=ph&test=plaintext
incluant 180 produits (en Java, C ou autre), Netty est placé 7^e pour
le débit, à `75 %` des performances du numéro un. Le banc d'essai
mesure le nombre de réponses par seconde d'un serveur minimal, ce qui
donne une idée du surcoût imposé par le produit dans des conditions
ultra-simples. Tout ça pour dire que oui Netty ça dépote.



== Conclusion

Netty offre un modèle de programmation simple et cohérent pour de la
programmation réseau de bas niveau, en tirant le meilleur parti des
ressources système. C'est l'occasion de repenser le rapport entre le
code applicatif et des protocoles réseau dont les conteneurs
d'application classiques s'efforcent de masquer la complexité. Au
moins avec Netty on est sûr de ne pas perdre son temps à contourner
des mécanismes d'abstraction trop limités. On a également les mains
libres pour créer les siens.

Netty offre également l'occasion de faire le ménage dans sa boîte à
outils, en remplaçant divers produits spécialisés par un seul.

La prise en main de Netty est assez ardue, et pour une documentation
de plus haut niveau que la Javadoc il faut souvent se référer à des
sources externes au site. Ce qui sauve tout, c'est la qualité du code
source.

Dans une série d'articles à venir (ça sera le cadeau pour la nouvelle
année) nous verrons comment un conteneur d'application cohérent avec
le modèle de programmation de Netty structure le code applicatif.
Reply all
Reply to author
Forward
0 new messages