Sortie d’Erlang/OTP 24

33
23
juil.
2021
Programmation fonctionnelle

À l’occasion de la sortie d’Erlang/OTP 24, le 12 mai 2021, je voudrais vous présenter cet environnement de programmation : le langage Erlang et son écosystème.

Écosystème

Erlang/OTP est la partie centrale d’un écosystème constitué de langages (fonctionnels), d’environnement d’exécution (machine virtuelle, JIT, etc.) et de bibliothèques standards.

Langages

Le langage Erlang, dont la première version date de 1987, a été développé originellement chez Ericsson pour programmer des commutateurs. Il est devenu open source en 1998. Ses premiers auteurs étaient Joe Armstrong et Robert Virding. Erlang a longtemps été connu pour sa robustesse : l’ATM AXD 301 d’Ericsson, programmé en erlang, a démontré offrir un taux de disponibilité de 99,9999999% (« neuf neufs »).

Ses principales caractéristiques :

  • une syntaxe inspirée de Prolog ;
  • le partage de données par passage de message ;
  • une gestion explicite du temps.

La syntaxe ayant été longtemps considérée comme un point faible, une trentaine de langages ont été ensuite développés qui bénéficient du même environnement d’exécution et offrent un certain niveau de compatibilité entre eux (réutilisation des bibliothèques), par exemple :

  • Elixir : inspiré par Ruby et offrant de larges possibilités de méta-programmation ;
  • lfe : inspiré par LISP ;
  • hamler : inspiré par Haskell.

Environnement d’exécution

Jusqu’à Erlang/OTP 23, l’environnement d’exécution le plus courant s’appelait la BEAM. C’est une machine virtuelle qui interprète le bytecode généré, par exemple, par le compilateur Erlang dont les principales caractéristiques sont :

  • distribution : plusieurs instances (« node ») de BEAM peuvent communiquer entre eux. Le passage de message entre processus erlang se fait de manière transparente au sein d’un même nœuds ou entre différents nœuds ;
  • immutabilité : l’immutabilité des variables est gérée par BEAM ;
  • remplacement de code à chaud : chaque pièce de code peut être remplacée à chaud, à l’échelle du module, la bibliothèque standard offrant des mécanismes pour gérer la migration de structure de données d’une version d’un module à l’autre, le cas échéant ;
  • gestion des threads : bien avant Go ou Scala, BEAM a intégré la gestion de threads ultra-léger, permise par le côté fonctionnel du langage. L’absence de structures partagées entre les processus permet une gestion ultra optimisée.

À propos des threads, Joe Armstrong avait publié un benchmark Java/Erlang pour comparer la gestion des threads. Vous pouvez trouver ici une version mise à jour du code Java, accompagnée de mises en œuvres dans d’autres langages du même algorithme. Sur une même machine, la version Java fait exploser le noyau Linux à quelques milliers de threads, quand BEAM supporte plusieurs millions de threads.

À part BEAM, le code Erlang peut être exécuté :

  • nativement, grâce au compilateur HiPE ;
  • grâce à un compilateur JIT ;
  • sur d’autres environnements d’exécutions par des projets plus ou moins maintenus, plus ou moins expérimentaux : erjang (erlang on JVM), Jerlang (erlang on JVM), erlscripten (Javascript), etc.

Bibliothèques standards

L’ensemble de bibliothèques standards pour Erlang s’appelle OTP (pour « Open Telephony Platform »). Parmi ses nombreuses bibliothèques, on peut citer, outre les classiques structures de données :

  • outils de développement/d’exécution : eunit (tests), système de releases, compilateur, génération de documentation, profiling, debugger distribué, analyseur statique de code, shell avec interpréteur ;
  • protocoles réseaux: SNMP, SSH (serveur/client), HTTP (serveur/client), LDAP, FTP, TFTP, etc. ;
  • base de données relationnelle distribuée : mnesia, totalement intégrée au langage, elle peut remplacer une base SQL classique pour des volumes de données modérés (< 2GB / table).

Applications

Au vu de ses caractéristiques, il semble naturel que le langage Erlang soit présent dans les infrastructures réseau et moins sur votre « desktop ». Les applications open source les plus connues sont :

  • MongooseIM / ejabberd: serveur XMPP hautement distribue, rendu populaire par son déploiement par WhatsApp ;
  • RabbitMQ, une mise en œuvre de AMQP (entre autres protocoles pris en charge) ;
  • CouchDB, base de données NoSQL distribuée.

Erlang/OTP 24

Compilation / environnement d’exécution

La principale nouveauté d’Erlang/OTP 24 est l’activation du JIT sur les architectures x86_64.

Réseau

  • Support de FTPES (« Explicit FTP over TLS ») ;
  • support de certificats EdDSA pour les connexions TLS ;
  • poignée de main TLS exécutée en parallèle.

Graphique

Ce n’est pas l’aspect le plus connu d’Erlang, mais OTP intègre depuis longtemps un binding WxWidget pour développer des interfaces graphiques :

  • migration vers wxWidgets 3 ;
  • ajoute le support de wxWebView.

Aller plus loin

  • # s/MQTT/AMQP/g

    Posté par  . Évalué à 1 (+1/-0).

    Bonjour,

    RabbitMQ, une mise en œuvre de MQTT ;

    Je pense que l’auteur voulait plutôt parler du protocole AMQP (en version 0-9-1 ça reste le gros morceau dans le fonctionnement de ce broker)

    diapo
    diapo

    Non ?

    (très bonne dépềche au demeurant, merci beaucoup !)

  • # Comparatif avec d'autres langages orientés "actor model"

    Posté par  (site Web personnel) . Évalué à 1 (+0/-0).

    Merci pour cette dépêche, cela fait un moment que je me dis que je dois regarder l'écosystème Erlang, la j'ai un point de départ avec Elixir <troll de vieux> et avec un bon mode emacs, cela devrait être top.</troll>

    As-tu vu des comparatifs de test en charge avec d'autres systèmes orientés async (actor model), je pense notamment à Golang qui semble s'inspirer du CSP (cher aux yeux d'un de nos enseignants à Lannion si je me souviens bien) ?

    • [^] # Re: Comparatif avec d'autres langages orientés "actor model"

      Posté par  (site Web personnel) . Évalué à 3 (+2/-0).

      De l'avis même de Joe Armstrong, elixir est le langage qu'il aurait écrit s'il avait dû réécrire erlang.
      La compatibilité avec les codes erlang existants est parfaite. Et surtout, la méta-programmation est "first-class citizen". Par exemple, pour définir une fonction "dictionnaire" à partir d'un fichier, on pourrait faire ceci:

      for {key, value} <- CSV.read("myfile.csv") do
      def get(unquote(key)), do: unquote(value)
      end

      Mais cela ne se limite pas à des macros de ce style, on peut vraiment écrire des langages complets.

      J'ai longtemps utilisé emacs avec les modes erlang ou elixir, qui fonctionnent plutôt bien, surtout depuis l'arrivée du protocole LSP (Language Server Protocol). Malheureusement, si on veut la compilation à la volée, complétion de code, doc en ligne, etc, la config devient tellement touffue qu'on peut facilement s'y perdre :/

      En ce qui concerne les perfs, le benchmark que je cite est un point départ, surtout pour les ordres de grandeur. Les 2-3% de perfs entre une plateforme et une autre ne sont pas significatifs, à mon avis, car il faut prendre l'écosystème dans son ensemble (tolérance aux pannes ? distribution ?).

      Ceci dit, le benchmark identifie clairement 2 "familles": les threads légers gérés le runtime lui-même (Scala, Go, erlang) qui gèrent facilement des millions de threads et les langages qui laissent Linux gérer les threads: ceux-là (Linux en fait) ne passent pas l'échelle 103 ou 104.

      Et sinon, si c'est de lui que tu parles, je garde effectivement un très bon souvenir de Marc Guyomard, qui m'a toujours inspiré dans mon métier :)

      "Liberté, Sécurité et Responsabilité sont les trois pointes d'un impossible triangle" Isabelle Autissier

      • [^] # Re: Comparatif avec d'autres langages orientés "actor model"

        Posté par  (site Web personnel) . Évalué à 2 (+1/-0). Dernière modification le 27/07/21 à 10:07.

        Merci pour les infos, et effectivement le LSP m'a beaucoup simplifié la vie :)

        Je n'ai jamais été à l'aise à l'idée de parler de threads pour les « fonctions asynchrones » d'un langage ou d'un framework. Peut-être est-ce à cause de mon côté C++ et/ou linux système… Mais il est clair que ces threads sont encore bien plus légers que ceux du système et permettent la description d'un système/appli de façon élégante et efficace.

        Merci de m'avoir rappeler le nom de Marc Guyomard, je n'avais eu qu'un seul module avec lui, justement sur au sujet du CSP…

        • [^] # Re: Comparatif avec d'autres langages orientés "actor model"

          Posté par  (site Web personnel) . Évalué à 2 (+1/-0).

          Les threads sont hyper optimisés par design, pas (que) à cause de la mise en œuvre. Comme le langage est fonctionnel, la communication par passage de message, etc le contexte d'un thread est hyper léger. Pas de structure partagée, de mutex, etc. Même pas de "file descriptors", etc (les I/O se font en envoyant un message à un thread chargé de la ressource voulue).

          À l'arrivée, on a un modèle acteur fonctionnel: on peut très bien (si l'envie nous prend) programmer entièrement en objet avec chaque objet étant un thread, vu qu'ils ne coûtent rien.

          "Liberté, Sécurité et Responsabilité sont les trois pointes d'un impossible triangle" Isabelle Autissier

          • [^] # Re: Comparatif avec d'autres langages orientés "actor model"

            Posté par  (site Web personnel) . Évalué à 3 (+0/-0).

            on peut très bien (si l'envie nous prend) programmer entièrement en objet avec chaque objet étant un thread, vu qu'ils ne coûtent rien.

            Ça coûte 326 mots par process, c'est peu pour un thread, mais pour un objet c'est énorme.

            Le post ci-dessus est une grosse connerie, ne le lisez pas sérieusement.

            • [^] # Re: Comparatif avec d'autres langages orientés "actor model"

              Posté par  (site Web personnel) . Évalué à 3 (+2/-0).

              Je parle d'ordre de grandeur ici.

              Un modèle à acteurs peut être mis en œuvre avec des langages tels que erlang, go, alors qu'il est quasiment inenvisageable avec des threads classiques.

              Ce qu'on observe en faisant tourner le benchmark cité plus haut (https://github.com/jeanparpaillon/actors_benchmark) est que, avec erlang:
              * le temps de 'fork' est constant / nb threads;
              * le temps du passage de message est ~quasiment~ constant également / nb threads.

              Allez, pour être plus précis, le résultat du benchmark avec 103, 104, 105, 106 et 107 threads:

              $ ZOG_THREADS=1000 make
              erl +P 1200 -noshell -eval 'zog:start('1000','1000').'
              Setup : 0.001 s (1.001001001001001 us per spawn) (999 spawns)
              Run   : 0.003 s (1.5007503751875937 us per msg) (1999 msgs)
              $ ZOG_THREADS=10000 make
              erl +P 10200 -noshell -eval 'zog:start('10000','1000').'
              Setup : 0.017 s (1.7001700170017002 us per spawn) (9999 spawns)
              Run   : 0.018 s (1.8001800180018002 us per msg) (9999 msgs)
              $ ZOG_THREADS=1000000 make
              erl +P 1000200 -noshell -eval 'zog:start('1000000','1000').'
              Setup : 1.355 s (1.355001355001355 us per spawn) (999999 spawns)
              Run   : 1.255 s (1.255001255001255 us per msg) (999999 msgs)
              $ ZOG_THREADS=10000000 make
              erl +P 10000200 -noshell -eval 'zog:start('10000000','1000').'
              Setup : 23.255 s (2.325500232550023 us per spawn) (9999999 spawns)
              Run   : 27.24 s (2.724000272400027 us per msg) (9999999 msgs)

              On voit une augmentation du temps de 'fork' (spawn) et passage de message entre 1 et 10 millions de threads (CPU i7-7820HQ, 32GB RAM).

              Au passage, je suis preneur de toute PR pour étoffer ce benchmark avec d'autres mises en œuvres / langages. (Attention, avec Java, la machine peut vite se freezer…).

              "Liberté, Sécurité et Responsabilité sont les trois pointes d'un impossible triangle" Isabelle Autissier

              • [^] # Re: Comparatif avec d'autres langages orientés "actor model"

                Posté par  (site Web personnel) . Évalué à 1 (+0/-0).

                Update: l'augmentation des temps de spawn/passage de message est bien lié au moment où la mémoire est remplie et commence le swap. Avec 5 millions de threads, sur ma machine, le temps reste constant.

                "Liberté, Sécurité et Responsabilité sont les trois pointes d'un impossible triangle" Isabelle Autissier

                • [^] # Re: Comparatif avec d'autres langages orientés "actor model"

                  Posté par  . Évalué à 2 (+0/-0).

                  Comment est-ce que les threads légers peuvent utiliser plusieurs nœuds de calcul ? L'ordonnanceur noyau prend un thread système d'un processus et lui donne un temps d'exécution sur un CPU. Il ne voit pas les threads légers et ne peut donc les exécuter en parallèle sur des cores distincts.

                  Je crois savoir que go crée un thread système/cpu et associe les threads légers à ces threads systèmes1.

                  Si beam fait pareil il n'y a pas de problème si le profile de travail n'est pas homogène entre les paquets de threads légers par exemple ? C'est un truc au quel il faut faire attention ? Par exemple ne pas créer tous les threads cpu intensive au même moment ou quelque chose du genre ?

                  Tu parle de passage de messages constant ça me parait aller à l'encontre de la démarche d'erlang, l'objectif du passage de message c'est d'avoir un couplage faible et de gérer les latences élevées par exemple parce que tu ne sais pas si le destinataire de ton message est sur le même thread léger/instance que toi. Si dans bien des cas c'est très performant si on souhaite que ce soit transparent il faut pas trop s'appuyer sur cette performance au risque d'avoir des problèmes quand on quitte les tests sur sa machine et qu'on passe dans un environnement cluster.

                  Il me semble que s'orienter vers des threads léger est surtout utile pour des workloads qui nécessitent un très grand nombre de petits I/O. C'est à dire quand ce qui freine l'application c'est la latence des I/O et pas le temps de calcul par exemple. En effet l'ordonnanceur en espace utilisateur ne préempte pas les threads léger et va s'appuyer sur leurs I/O pour les commuter. Ça ne paraît pas idiot pour des télécoms et pour rabbitmq d'avoir ce genre de besoins d'ailleurs.

                  Tu as des retours sur comment c'est implémenter et sur dans quel cas ça marche moins bien ?



                  1. J'y pense après coup une autre solution, celle de node, est de ne pas gérer de parallélisme du tout et de lancer un nodejs par cpu pour pouvoir être parallèle. 

                  • [^] # Re: Comparatif avec d'autres langages orientés "actor model"

                    Posté par  (site Web personnel) . Évalué à 4 (+3/-0).

                    Il y un papier assez complet sur le sujet: http://www.erlang.se/euc/08/euc_smp.pdf

                    Pendant longtemps, BEAM utilisait un ordonnanceur (scheduler) interne et un thread système. Le système (Linux, par exemple), ne voit qu'un seul thread.
                    Avec la massification des systèmes SMP/multicore, la BEAM démarre (par défaut) un thread par core, un scheduler par thread système.

                    Par défaut, les processus erlang ne sont pas liés à un scheduler particulier, et les schedulers piochant dans une run list partagée.

                    Go a une mise en œuvre similaire.

                    Pour le passage de message en temps constant, c'est dans le cadre d'un benchmark s'exécutant bien sûr toujours sur la même machine. Cela démontre juste que le nombre de processus erlang ne crée pas un 'overhead' impactant les performances.

                    Par contre, effectivement, erlang a été conçue dès le début pour être extrêmement tolérant aux pannes et, donc, aucune garantie sur le temps de délivrance de message n'est donnée, pas même de garantie de délivrance. Au sein d'un même 'node', on peut raisonnablement estimer que les messages sont tous délivrés, mais ce n'est pas le cas entre différents 'node' (qui communiquent par défaut en UDP). D'où également une gestion explicite du temps qui permet d'avoir un système toujours réactif. Par défaut, toute attente de message est limitée par un timeout.

                    En ce qui concerne les perfs des I/O, c'est effectivement une limitation d'erlang. Limitation assez théorique d'ailleurs puisque:
                    1. des optimisations sont possibles, et ont été mises en œuvre: les messages ne sont copiés que si vraiment nécessaires (modification ou transfert sur un autre noeud);
                    2. erlang intègre la possibilité de mapper des fonctions erlang sur des fonctions écrites en C. C'est même considéré comme une bonne pratique pour peu que le code soit circonscrit, bien testé :P et interagisse avec les scheduler pour ne pas bloquer les autres processus (lire Respecting the scheduler in erlang NIFs.

                    "Liberté, Sécurité et Responsabilité sont les trois pointes d'un impossible triangle" Isabelle Autissier

                    • [^] # Re: Comparatif avec d'autres langages orientés "actor model"

                      Posté par  . Évalué à 3 (+1/-0).

                      Merci pour tes précisions.

                      En ce qui concerne les perfs des I/O, c'est effectivement une limitation d'erlang.

                      Ce n'est pas ce que je voulais dire. Je disais qu'il me semble que c'est un modèle de programmation surtout fait pour des programmes I/O bound (et même latence bound). Les threads légers sont conçu (il me semble, hein) pour gérer des I/O concurrentes et pas pour de la parallélisation. Si je prend par exemple la génération d'une rainbow table (premier truc qui me vient en tête cpu bound parallélisable) il n'y a pas d'intérêt (en tout cas en terme de perf) de spawn un thread léger par entrée plutôt que d'itérer avec un threads système par cpu.

                      erlang intègre la possibilité de mapper des fonctions erlang sur des fonctions écrites en C. C'est même considéré comme une bonne pratique pour peu que le code soit circonscrit, bien testé :P et interagisse avec les scheduler pour ne pas bloquer les autres processus (lire Respecting the scheduler in erlang NIFs.

                      J'ai pas encore lu le lien, mais ça me parait très casse gueule. Il n'y a pas de raison particulière qu'une bibliothèque C respecte le scheduler erlang… Il n'y a pas moyen de plutôt faire interagir ça avec un passage de message ? Tu as du code erlang/C qui attend/envoi des messages et qui se fout du scheduler erlang et le reste du programme vie sa meilleure vie ? Ça demande soit des api particulières (par exemple avec un binding C du protocole udp des messages) soit de pouvoir créer un thread qui ne soit pas géré par l'ordonnanceur erlang.

                      C'est comme ça que bosse elm toute interaction avec du js se fait par envoi de message et il y a une api js d'envoi/réception de message. Je trouve ça très propre.

                      En tout cas merci je vais regarder les liens que tu m'a donné.

                      • [^] # Re: Comparatif avec d'autres langages orientés "actor model"

                        Posté par  (site Web personnel) . Évalué à 3 (+2/-0).

                        Les NIFs sont casse-gueule, oui. Mais on ne parle pas ici de s'interfacer avec des usines à gaz. On parle ici de code très spécifiques qui sera livré "enrobé" avec leur glue erlang.

                        Un exemple qui me vient en tête serait un calcul sur une grande matrice:
                        * le code C charge la matrice,
                        * il fournit quelques point d'entrée pour déclencher un calcul, récupérer son résultat,
                        * les données de la matrice ne sont pas transférés par message, seules des commandes (charger, calculer, etc) et le résultat.

                        Pour respecter le scheduler:
                        * soit le point d'entrée est assuré de ne pas bloquer pour un temps "déraisonnable" par rapport au scheduler;
                        * soit le point d'entrée risque d'affamer les autres processus, et la librairie erlang pour écrire ces NIFs fournit des primitives pour rendre la main à l'ordonnanceur, qui décidera ou non d'ordonnancer un autre processus.

                        Si plusieurs instances de ce code doivent s'exécuter, on privilégiera bien sûr de créer des processus erlang qui appelleront le NIF. La parallélisme sera donc géré par erlang et non pas dans le code C.

                        Au passage, erlang fournit également un mécanisme qui ressemble plus à des RPC pour interagir avec du code dans différents langages (les "ports"):
                        * le code externe est lancé dans un processus distinct, supervisé par erlang;
                        * erlang et ce processus échangent des messages comme 2 processus erlang, à travers stdin/out pour le code externe, des librairies dans différents langages (C et Java sont officiellement supportés) fournissent les mécanismes de (de)sérialisation.

                        La latence est un tout petit peu moins bonne que pour le NIF. En revanche, si le code externe crash, la VM erlang le redémarrera sans aucun problème. Dans le cas d'un NIF, un crash dans le NIF occasionne un crash de la VM.

                        "Liberté, Sécurité et Responsabilité sont les trois pointes d'un impossible triangle" Isabelle Autissier

                        • [^] # Re: Comparatif avec d'autres langages orientés "actor model"

                          Posté par  . Évalué à 2 (+0/-0).

                          soit le point d'entrée risque d'affamer les autres processus, et la librairie erlang pour écrire ces NIFs fournit des primitives pour rendre la main à l'ordonnanceur, qui décidera ou non d'ordonnancer un autre processus.

                          C'est ça qui me semble rapidement poser problème. Ce qui va pousser à utiliser du C c'est pour être CPU intenssive ou pour t'interfacer avec du matériel ou du code qui ne tourne pas sur beam. Un code de calcul par exemple que tu ne peux ou ne veux pas retoucher pour l'ordonnanceur erlang (et ce n'est pas forcément simple de savoir quand c'est pertinent de se préempter ça peut ruiner les perf). D'où ma question pour la seconde solution. Après c'est questionnable il peut être plus intéressant de vraiment séparer et d'avoir des interactions à coup message queue entre les 2 (pas forcément amqp).

                          • [^] # Re: Comparatif avec d'autres langages orientés "actor model"

                            Posté par  (site Web personnel) . Évalué à 2 (+1/-0). Dernière modification le 30/07/21 à 10:21.

                            Je pense que séparer et communiquer à travers des interfaces claires te fait largement gagner en maintenabilité ce que tu peux perdre en latence. Par contre, je n'ai jamais vu l'intérêt d'utiliser AMQP avec erlang: tu peux avoir l'équivalent d'un broker de service en quelques lignes de code, avec une latence qui n'a rien à voir avec un service externe (RabbitMQ).

                            Au final, que ce soit en 100% erlang ou avec des codes externes, les applications erlang sont très similaires à des micro-services, sans k8s/docker, des load balancer HTTP, des API REST, etc.

                            Dans les architectures que je déploie, j'ai tendance à privilégier maintenant des RPC plus agnostique par rapport au langage (gRPC, protobuf sur CoAP, voir BERT, etc) mais en aucun cas un broker de message externe.

                            "Liberté, Sécurité et Responsabilité sont les trois pointes d'un impossible triangle" Isabelle Autissier

                            • [^] # Re: Comparatif avec d'autres langages orientés "actor model"

                              Posté par  . Évalué à 3 (+2/-1).

                              Par contre, je n'ai jamais vu l'intérêt d'utiliser AMQP avec erlang: tu peux avoir l'équivalent d'un broker de service en quelques lignes de code, avec une latence qui n'a rien à voir avec un service externe (RabbitMQ).

                              Le reroutage à chaud des messages par exemple, le monitoring, tout un tas de règles de gestions que tu peux ajouter au runtime pour que ce soit différent par environnement (taille limite des queues par exemple), tu peux aussi ne pas vouloir stocker un nombre important de messages dans la mémoire de ton processus applicatif, être agnostique des langages avec une pléthore de langages et outils déjà existant, maintenant ils font du log distribué à la kafka,… Je ne doute pas que ça soit pas très compliqué à faire en erlang, mais il faut prévoir les choses avant et c'est toujours de la complexité en plus.

                              Au final, que ce soit en 100% erlang ou avec des codes externes, les applications erlang sont très similaires à des micro-services, sans k8s/docker, des load balancer HTTP, des API REST, etc.

                              C'est pas la même granularité. On peut dire qu'OSGi c'était aussi des microservices avant l'heure. Mais ça a tout de même des différences importantes en terme de déploiement et de ce que ça implique.

                              Dans les architectures que je déploie, j'ai tendance à privilégier maintenant des RPC plus agnostique par rapport au langage (gRPC, protobuf sur CoAP, voir BERT, etc) mais en aucun cas un broker de message externe.

                              Ça veut dire que :

                              • tu ne fais que du requête/réponse
                              • des messages one to one
                              • que tu ne peux pas retirer le destinataire
                              • que c'est celui qui envoi le message qui doit gérer la stratégie de rejeu d'un message
                              • que tu ne peut pas lisser ta charge en empilant des messages dans une fille ou si tu le fait c'est plus fragile parce que la file est géré par le destinataire et pas commune à plusieurs instances du destinataire

                              Du coup oui je comprends que tu n'es pas besoin de broker de messages :)

                              • [^] # Re: Comparatif avec d'autres langages orientés "actor model"

                                Posté par  (site Web personnel) . Évalué à 2 (+0/-1). Dernière modification le 30/07/21 à 15:14.

                                L'avantage c'est que le stagiaire de prod sait quoi faire quand ça plante: redémarrer. Ca tombe bien, c'est le seul truc qu'il sait faire.

                                Alors qu'avec un kubernetes et du kafka, ben il laisse tout en rade jusqu'au retour des gens qui sachent.

                                Le post ci-dessus est une grosse connerie, ne le lisez pas sérieusement.

                                • [^] # Re: Comparatif avec d'autres langages orientés "actor model"

                                  Posté par  . Évalué à 1 (+0/-1).

                                  Je sais pas pourquoi tu viens me chercher sur kubernetes, mais kerbernetes n'a pas besoin d'un stagiaire pour redémarrer quand ça plante (et on l'a pas attendu pour inventer les watchdogs).

                                  Après je ne sais pas si :

                                  • laisser un stagiaire seul face à une prod est quelque chose de raisonnable surtout si redémarrer est la seule chose qu'il sait faire
                                  • juger une techno à ce qu'un stagiaire présumé bête sait en faire est quelque chose de pertinent
                                  • maintenir des travaux manuels facilement automatisables pour laisser du travail à des gens présumé ne rien comprendre est une bonne idée

                                  Après chacun gère sa prod comme il l'entends.

                              • [^] # Re: Comparatif avec d'autres langages orientés "actor model"

                                Posté par  (site Web personnel) . Évalué à 1 (+0/-0).

                                Je n'ai absolument rien contre RabbitMQ et je pense qu'il y a des architectures où il est pertinent.

                                Mais je suis plutôt partisan des approches 'lean' où on évite des usines à gaz pour 2-3 fonctionnalités qui nous intéressent. Il est parfois plus rapide de coder une fonctionnalité que d'apprendre à utiliser (et maintenir, et déployer, etc) des outils complexes. Je ne suis pas non plus partisan de l'approche inverse que consiste à tout coder tout seul.

                                tu ne fais que du requête/réponse

                                Non, c'est juste le cas pour gRPC, pas pour BERT ou du protobuf sur TCP (voire UDP), par exemple

                                one-to-one

                                Voir le module elixir Registry.

                                que tu ne peux pas retirer le destinataire

                                Grâce au mécanisme de supervision des processus, le module Registry ci-dessus gère parfaitement la connexion/reconnexion d'un client. Est-ce cela dont tu parles ?

                                que c'est celui qui envoi le message qui doit gérer la stratégie de rejeu d'un message

                                Oui, sauf que mettre en œuvre une stratégie à base de timeouts, de retries, voire de files d'attentes différentiées se fait en quelques dizaines de lignes de code, à comparer à la configuration d'un outil comme RabbitMQ (+ configuration du code client).

                                que tu ne peut pas lisser ta charge en empilant des messages dans une fille ou si tu le fait c'est plus fragile parce que la file est géré par le destinataire et pas commune à plusieurs instances du destinataire

                                Voir GenStage

                                GenStage gère très bien le back pressure.

                                Je n'ai rien contre RabbitMQ (écrit en erlang en plus :D ) mais j'ai vu plusieurs cas où son utilisation n'était pas justifiée et créait un overhead énorme, pas seulement en terme de latence mais surtout en terme d'intégration et de maintenance.

                                "Liberté, Sécurité et Responsabilité sont les trois pointes d'un impossible triangle" Isabelle Autissier

                                • [^] # Re: Comparatif avec d'autres langages orientés "actor model"

                                  Posté par  . Évalué à 2 (+1/-1). Dernière modification le 30/07/21 à 19:10.

                                  Mais je suis plutôt partisan des approches 'lean' où on évite des usines à gaz pour 2-3 fonctionnalités qui nous intéressent.

                                  J'évite de penser de cette façon. Les mots clefs n'ont pas toujours du sens en soit. Est-ce que c'est lean d'avoir de la complexité un peu partout plutôt que de regrouper tout ce sujet à un seul endroit ? Ça dépend de pleins de choses.

                                  Il est parfois plus rapide de coder une fonctionnalité que d'apprendre à utiliser (et maintenir, et déployer, etc) des outils complexes. Je ne suis pas non plus partisan de l'approche inverse que consiste à tout coder tout seul.

                                  C'est aussi pas simple de faire un passage de l'un à l'autre. Mais pour l'exemple le fait de regrouper tout cette problématique au même endroit permet d'avoir un comportement homogène partout.

                                  one-to-one

                                  Voir le module elixir Registry.

                                  Est-ce que c'est à l'émetteur de se soucier de la liste de ses destinataires ? C'est un choix. 'fin c'est un choix jusqu'à ce que tu ai un grand nombre de destinataires ou que la liste des destinataires soit très mouvant.

                                  que tu ne peux pas retirer le destinataire

                                  Grâce au mécanisme de supervision des processus, le module Registry ci-dessus gère parfaitement la connexion/reconnexion d'un client. Est-ce cela dont tu parles ?

                                  Tu garde un couplage entre l'emmeteur et le destinataire. Le plantage/mise à jour du destinataire impose un travail en plus à l'émetteur.

                                  Oui, sauf que mettre en œuvre une stratégie à base de timeouts, de retries, voire de files d'attentes différentiées se fait en quelques dizaines de lignes de code, à comparer à la configuration d'un outil comme RabbitMQ (+ configuration du code client).

                                  Je me doute, mais elle a donc de potentiels bugs, doit donc être pensée complètement en amont (tu as bien moins de flexibilité lors de ton run). La configuration d'un client rabbitmq c'est quelques lignes (et vraiment on parle en ligne et pas en dizaines de lignes) quelque soit le langage. La configuration d'un rabbitmq c'est fait une bonne fois pour toute.

                                  que tu ne peut pas lisser ta charge en empilant des messages dans une fille ou si tu le fait c'est plus fragile parce que la file est géré par le destinataire et pas commune à plusieurs instances du destinataire

                                  Voir GenStage

                                  Si j'ai bien compris c'est l'émetteur qui doit gérer la disponibilité ou non du destinataire.

                                  GenStage gère très bien le back pressure.

                                  C'est très cool, ça n'est pas naturel avec AMQP (ça l'est déjà bien plus avec kafka).

                                  Mais la back pressure ce n'est pas forcément le lissage de charge. Tu peux avoir du code qui reçoit des requêtes à un débit qui est acceptable pour toi, mais qui doit transmettre à quelqu'un d'autres qui a plus de mal. Si c'est intermittent le fait de laisser un brocker stocker les messages pour les donner tranquillement au destinataire c'est pertinent. Si tu veux être élastique de scale les destinataires en fonction de ce que tu empile, tu dois faire la remonté d'alerte ou de monitoring à la main pour ensuite avoir une autre partie qui gère tes déploiement. Avec des brocker ça peut se faire avec un peu de conf et se faire sur tout ou partie du système sans modification du code.

                                  Je n'ai rien contre RabbitMQ (écrit en erlang en plus :D ) mais j'ai vu plusieurs cas où son utilisation n'était pas justifiée et créait un overhead énorme, pas seulement en terme de latence mais surtout en terme d'intégration et de maintenance.

                                  Moi je n'ai pas d'action chez eux. Je répond juste à ton message qui ne voyais pas d'intérêt. Tu te retrouve à tout faire à la main (avec une certaine simplicité mais l'ajout de différentes bibliothèques) et tu n'es plus agnostique. Et surtout tu fais un choix de où tu place les responsabilités (avec ce que ça implique dans un sens comme dans l'autre). Sans aller dans l'idée qu'il faut que tout soit toujours agnostique, je me fais de petits outils amqp qui ne sont pas avec le même langage que le reste et j'en suis bien content.

                                  Après je ne te parle pas des workflows surprenant comme router du trafic environnement pour des tests/expérimentations/bench qui te permettent de récupérer du contenu d'un environnement pour le jouer localement ou sur un autre environnement sans avoir à ajouter du code dédié à ton expérimentation.

  • # Comment bien débuter

    Posté par  . Évalué à 3 (+2/-0).

    J'avais déjà fait quelques recherches sur Erlang lors de la précédente dépêche.

    C'est vrai qu'il y a des fonctionnalités très intéressantes avec ce langage. J'ai d'ailleurs vu qu'il existait aussi Elixir, un Erlang plus simple au niveau de la syntaxe si j'ai bien tout compris ? D'ailleurs faut-il choisir Erlang ou Elixir ? Ou les deux ?

    J'aimerais bien m'y mettre mais je ne sais pas trop par où commencer… Livres ? Sites web ?

    Après avoir découvert les bases du langage je me serais bien fait la main en convertissant un logiciel de backup décentralisé que j'ai développé.

    Merci d'avance pour les infos ;)

  • # temps explicite ?

    Posté par  . Évalué à 3 (+1/-0).

    une gestion explicite du temps.

    C'est à dire ?

    • [^] # Re: temps explicite ?

      Posté par  (site Web personnel) . Évalué à 4 (+3/-0).

      La gestion du temps est possible grâce à une expression du langage, pas à travers l'appel à des fonctions. Cette expression est receive.

      Par exemple:

      receive 
        message1 -> 
          do_something(message1);
      
        message2 ->
          do_something_else(message2)
      
      after
        5000 ->
          cant_wait_anymore()
      end.

      Ici, on attend 2 types de message (message1 ou message2) pendant 5sec (5000ms). Si aucun des 2 n'est reçu, on appelle la fonction cant_wait_anymore(). C'est une structure de base de langage sur laquelle des librairies plus complexes sont construites.

      Une mise en œuvre de sleep(timeout) pourrait être:

      receive
        non_existing_message ->
          ok
      
      after
        Timeout ->
          ok
      end.

      On attend un message qui n'arrivera jamais pendant Timeout ms.

      "Liberté, Sécurité et Responsabilité sont les trois pointes d'un impossible triangle" Isabelle Autissier

Envoyer un commentaire

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n’en sommes pas responsables.