Crossbar, le futur des applications Web Python ?


Je suis crossbar.io depuis quelques temps maintenant, et je suis très, très étonné de ne pas plus en entendre parler dans la communauté Python.

Bon, en fait, à moitié étonné.

D’un côté, c’est une techno qui, à mon sens, représente ce vers quoi Python doit se diriger pour faire copain-copain avec Go/NodeJs et proposer une “killer feature” dans le monde des applications serveurs complexes.

De l’autre, hum, leur page d’accueil explique à quoi ça sert de cette manière :

Crossbar.io is an application router which implements the Web Application Messaging Protocol (WAMP). WAMP provides asynchronous Remote Procedure Calls and Publish & Subscribe (with WebSocket being one transport option) and allows to connect application components in distributed systems

Moui, moui, moui monseigneur, mais concrètement, là, hein, je peux faire quoi avec ?

C’est toujours le problème avec les gens intelligents (hein Cortex ?) : ils font des trucs super cool, et personne ne comprend à quoi ça sert parce qu’il ne sont pas foutus de l’expliquer.

Moi je suis un peu con, alors je vais profiter qu’on soit tous au même niveau pour vous faire passer le message.

J’étais persuadé d’avoir mis la musique habituelle… Je la remets :

Web Application Message Protocol

Je vous avais parlé d’autobahn dernièrement, un client WAMP qui embarque aussi un routeur basique. Crossbar est la partie serveur, un routeur WAMP plus sérieux.

Crossbar permet à tous les clients d’échanger des messages WAMP à travers lui. Bien entendu, un client WAMP peut parler au serveur Crossbar et inversement comme un client HTTP peut parler à un serveur Apache/Nginx et inversement. Mais plus que ça, les clients peuvent parler entre eux, de manière transparente et simple. Comme un client AMQP peut parler aux autres à travers un serveur RabbitMQ.

Cependant ça ne vous avance pas si vous ne savez pas ce qu’est WAMP ou à quoi ça sert. La charrue avec la peau de l’ours, tout ça.

WAMP est un protocole standardisé pour échanger des messages entre deux systèmes. Ce n’est pas particulièrement lié à Python, on peut parler WAMP dans n’importe quel langage.

Il fonctionne en effet, principalement, au dessus de Websocket, donc on peut l’utiliser directement dans le navigateur, dans Firefox, Chrome, Opera, Safari et même IE10, via une lib Javascript. Qui est en fait juste un gros wrapper autour de l’API websocket standardisant la manière d’envoyer des données. Il n’y a rien de magique derrière, pas de formats compliqués, pas de binaire, c’est vraiment juste des appels websocket contenant des données formatées en JSON avec une certaine convention. En ce sens il fait penser à SockJS et (feu) socket.io.

Seulement contrairement aux solutions type SocketJS, il n’est pas limité au navigateur. Il y a des libs pour l’utiliser dans un code serveur Python, C++ ou NodeJS, dans une app Android et même directement depuis les entrailles de Nginx ou d’une base de données Oracle (en SQL) avec certains routeurs.

Schéma général du fonctionnement de WAMP

Comme HTTP, WAMP est juste un moyen de faire parvenir des données d’un point A à un point B. Mais contrairement à HTTP, WAMP permet aux clients de parler entre eux, et pas juste au serveur.

Comprenez bien, ça veut dire qu’on peut envoyer et recevoir des données arbitraires, en temps réel, entre tous ces systèmes, sans se prendre la tête, et de manière transparente.

WAMP c’est donc comme, mais mieux que :

  • des requêtes HTTP, car c’est du push, asynchrone et temps réel;
  • des requêtes websocket via SocketJS car c’est un standard, qui fonctionne SUR et ENTRE plusieurs services côté serveurs malgré les différents langages;
  • des messages AMQP car ça marche dans le navigateur et ça se configure facilement.

Bien utilisé, Crossbar permet d’amener Python dans la cour de frameworks “temps réel” novateurs comme MeteorJS, et potentiellement les dépasser.

Car WAMP permet de faire deux choses. Simplement. Et bien.

1 – Du PUB/SUB

Donc de dire dans son code “appelle cette fonction quand cet événement arrive”. C’est comme les signaux de Django ou QT, mais ça marche à travers le réseau. On le fait souvent avec Redis ces temps-ci. Avec WAMP et Javascript, ça donne :

// connection au routeur WAMP (par exemple, crossbar.io)
ab.connect("ws://localhost:9000", function(session) {
    // je m'inscris à un événement
    session.subscribe('un_evenement_qui_vous_plait', function(topic, evt){
        // faire un truc avec les données reçues
        // à chaque fois que l'événement est envoyé
        // par exemple mettre la page à jour
    });
});
Schéma de fonctionnement du subscribe de WAMP

Des client SUBscribe à un événément. Un événément est un nom arbitrairement choisit par le programmeur, et qu’il va déclencher lui-même quand il pense qu’il se passe quelque chose d’important auquel il faut que le reste du signal réagisse.

Et ailleurs, dans une autre partie du fichier, ou même sur un autre navigateur Web :

ab.connect("ws://localhost:9000", function(session) {
    // création d'un événement auquel on attache des données
    session.publish('un_evenement_qui_vous_plait', ['des données']);
Schéma de fonctionnement de PUB avec WAMP

Le programmeur décide que quelque chose d’important arrive (création d’un contenu, login d’un utilisateur, notification), et PUBlish l’événement

Et oui, c’est tout. On se connecte à crossbar, et on discute. La fonction du subscribe sera alors appelée avec les données du publish. Même si il y a 3000 km entre les deux codes. Même si le code A est sur un navigateur et le B sur un autre, ou sur un serveur NodeJS, ou une app Android.

Ce qui fait peur au début, c’est qu’il y a TROP de flexibilité :

  • Je dois attendre quoi comme événements ?
  • Qu’est-ce que je passe comme données ?
  • Est-ce que c’est rapide ? Léger ?

Mais en fait c’est super simple : un événement c’est juste une action de votre application comme un élément (un post, un commentaire, un utilisateur…) ajouté, supprimé ou modifié. Finalement c’est le bon vieux CRUD, mais en temps réel, et en push, au lieu du pull. Vous choisissez un nom qui représente cette action, vous attachez des données à ce nom, voilà, c’est un événement que tous les abonnés peuvent recevoir.

Avec un bonus : ça marche sur le serveur aussi ! Votre code Python reçoit “ajouter un commentaire” comme événement ? Il peut ajouter le commentaire en base de données, envoyer un message à un service de cache ou à un autre site en NodeJS pour le mettre à jour, renvoyer un événement pour mettre à jour les pages Web et l’app Android, etc.

On peut passer n’importe quelles données qui peut se JSONiser. En gros n’importe quoi qu’on enverrait via HTTP. Donc des données très structurées, imbriquées et complexes comme des données géographiques, ou très simples comme des notifications

Avec PUB / SUB, WAMP remplace tout ce qu’on ferait normalement avec des appels AJAX dans le browser, et tout ce qu’on ferait avec des files de message côté serveur. Plus puissant encore, il permet de relier ces deux mondes.

Et même si on atteint pas les perfs de ZeroMQ (qui n’a pas de serveur central), c’est très performant et léger.

2 – Du RPC

Appeler une fonction située ailleurs que dans son code. C’est vieux comme le monde (si vous avez des souvenirs douloureux de CORBA et SOAP, levez la main), et c’est extrêmement pratique. Pour faire simple, continuons avec un exemple en Javascript, mais rappelez-vous que ça marche pareil en C++ ou Python :

ab.connect("ws://localhost:9000", function(session) {
   function une_fonction(a, b) {
      return a + b;
   }
   // on déclare que cette fonction est appelable à distance
   session.register('une_fonction', une_fonction);
});
Schéma expliquant register avec WAMP

RPC marche à l’envers de PUB/SUB. Un client expose du code, et un autre demande explicitement qu’il soit exécuté.

Côté appelant :

ab.connect("ws://localhost:9000", function(session) {
    // on appelle la fonction à distance, on récupère une
    // promise qui nous permet de travailler sur le résultat
   session.call('une_fonction', 2, 3).then(
      function (res) {
         console.log(res);
      }
   );
Schéma expliquant CALL en WAMP

Contrairement à PUB/SUB, RPC ne concerne que deux clients à la fois. Mais ça reste asynchrone. Le client demandeur n’attend pas le résultat de l’appel de la fonction. Il est signalé par le serveur quand il est prêt.

Pareil que pour le PUB/SUB, les gens ont du mal à voir l’utilité à cause du trop de flexibilité que ça apporte. Imaginez que votre projet soit maintenant éclaté en de nombreux petits services qui tournent et qui sont indépendants :

  • Un service pour le site Web.
  • Un service d’authentification.
  • Un service pour l’API.
  • Un service pour les tâches longues.
  • Un service de monitoring et administration technique.

Tous ces services peuvent ainsi communiquer entre eux via RPC, mais n’ont pas besoin d’être dans le même processus. On peut profiter pleinement de tous les cœurs de sa machine, on peut même les mettre sur des serveurs séparés.

Mieux, avoir un service bloquant ne pénalise pas tout le système. En effet, un problème avec les systèmes asynchrones en Python est que beaucoup de libs sont encore bloquantes (typiquement les ORMs). Avec ce genre d’architecture, on peut créer un service qui ne fait que les appels bloquant et laisser les autres services non bloquant l’appeler de manière asynchrone. Pendant qu’il bloque, le reste du système peut traiter d’autres requêtes.

Crossbar, plus qu’un routeur WAMP

L’idée des concepteurs de crossbar est de permettre de créer des systèmes avec des services composables qui communiquent entre eux plutôt que tout dans un gros processus central. Ils ne se sont donc pas arrêtés au routing.

Crossar est également un gestionnaire de processus, comme supervisor ou, plus légitimement, circus (Tarek, fait une pause, vient ici !) et sa communication ZeroMQ.

Il se configure avec un simple fichier JSON, et on peut y définir des classes Python qui seront lancées dans un processus séparé et pourront discuter avec les autres clients via WAMP :

{
   "processes": [
      { // premier processus
         "type": "worker",
         "modules": [
            {
               un_worker.Classe
            },
            {
               un_autre_worker.Classe
            }
         ]
      },
      {  // second processus
         "type": "worker",
         "modules": [
            {
               un_autre_worker_dans_un_autre_process.Classe
            }
         ]
      }
   ]
}

Mais si ça ne suffit pas, on peut également lancer des programmes extérieurs non Python dont crossbar va gérer le cycle de vie :

{
   "processes": [
      {
         "type": "guest",
         "executable": "/usr/bin/node",
         "arguments": ["votre_script.js"],
         "stdout": "log"
      }
   ]
}

Vous avez donc ainsi les deux atouts pour avoir une architecture découplée, scalable, exploitant plusieurs cœurs, et compensant en partie les bibliothèques bloquantes :

  • Un protocole flexible, simple, qui permet à tout le monde se parler entre eux (WAMP).
  • Une API qui permet soit de réagir à un changement (PUB/SUB), soit de demander une action (RPC).
  • Un programme qui gère cette communication, et le cycle de vie des composants qui parlent entre eux.

Cas concret

WAMP est typiquement le genre de techno qui ne permet PAS de faire quelque chose qu’on ne faisait pas avant. Ce n’est pas nouveau.

En revanche, WAMP permet de le faire mieux et plus facilement.

Prenez le cas d’un utilisateur qui se connecte sur un forum. Il va sur un formulaire, il poste ses identifiants, ça recharge la page, il est connecté. Si les autres utilisateurs rechargent leurs pages, ils verront un utilisateur de plus connecté.

Si on veut rendre ça plus dynamique, il faut utiliser de l’AJAX, et si on veut avoir une mise à jour presque en temps réel, il faut faire des requêtes Ajax régulières. Ce qui est assez bancal et demande beaucoup de travail manuel.

Certains sites modernes utilisent Websocket, et des serveurs asynchrones comme NodeJS, et un routeur PUB/SUB comme Redis, pour faire cela de manière rapide et plus facile. L’application est très réactive. Mais le système est hétéroclite. Et si on veut envoyer des messages entre des composants serveurs, ça demande encore quelque chose de différent.

WAMP unifie tout ça. Un coup de RPC pour le login pour effectuer l’action:

Schéma d'un exemple concret de RPC WAMP

Notez que le RPC marche de n’importe quel client à n’importe quel client. Il n’y a pas de sens obligatoire. Le login est un exemple simple mais on peut faire des choses bien plus complexes.

Et un coup de PUB/SUB pour prévenir tout le monde que quelque chose s’est passé :

Schéma d'exemple concret de PUB/SUB avec WAMP

Je n’ai mis que des clients légers ici, mais je vous rappelle qu’un client peut être un serveur NodeJS, une base de données, un code C++…

Bien entendu, on pourrait faire ça avec les technos existantes. C’est juste moins pratique.

Notez également que Crossbar encourage à avoir un service qui ne se charge que du login, sans avoir à faire une usine à gaz pour cela. Si demain votre service de login a besoin d’être sur un coeur/serveur/une VM séparé pour des raisons de perfs ou de sécurité, c’est possible. Crossbar encourage ce genre de design.

Voici où est le piège

Car évidement, il y en a toujours un, putain de métier à la con.

Et c’est la jeunesse du projet.

Le projet est stable, le code marche, et les sources sont propres.

Mais la doc, mon dieu la doc… Les exemples sont pas à jour, il y a deux versions qui se battent en duel, on sait pas trop quelle partie sert à quoi.

Et comme tout projet jeune, l’API n’a pas été assez étudiée. Or la partie Python est basée sur Twisted, sans polish. Twisted, c’est puissant, c’est solide, et c’est aussi une API dégueulasse.

Un exemple ? Comment écouter un événement :

# Des imports légers
from twisted.python import log
from twisted.internet.defer import inlineCallbacks
 
from autobahn.twisted.wamp import ApplicationSession
from autobahn.twisted.wamp import ApplicationRunner
 
# Une bonne classe bien subtile pour copier Java
class ListenForEvent(ApplicationSession):
 
    # Deux méthodes de boiler plate obligatoires
    # et parfaitement soulantes pour l'utilisateur
    # final. Cachez moi ça bordel !
    def __init__(self, config):
        ApplicationSession.__init__(self)
        self.config = config
 
    def onConnect(self):
        self.join(self.config.realm)
 
    # Bon, on se décide, soit on fait une classe avec des noms
    # de méthode conventionnés, soit on met des décorateurs, 
    # mais pas les deux, pitié !
    @inlineCallbacks
    def onJoin(self, details):
        callback = lambda x: log.msg("Received event %s" % x)
        yield self.subscribe(callback, 'un_evenement')
 
# Python doit lancer explicitement un event loop.
# Ca pourrait (devrait) aussi être embeded dans une
# sousclasse de ApplicationSession.
# /me prend un colt. BANG !
if __name__ == '__main__':
   runner = ApplicationRunner(endpoint="tcp:127.0.0.1:8080",
                              url="ws://localhost:8080/ws",
                              realm="realm1")
   runner.run(ListenForEvent)

C’est la raison pour laquelle je vous ai montré le code JS et pas Python pour vous vendre le truc. Sur sametmax.com, on aura tout vu :(

Voilà à quoi devrait ressembler le code Python si l’API était mature :

from autobahn.app import App
 
app = App(url="ws://localhost:8080/ws")
 
@event("un_evenement")
def handle(details):
    app.log("Received event %s" % x)
 
if __name__ == '__main__':
   app.run()

Chose que je vais proposer sur la mailing list (ils sont réactifs et sympas, vive les allemands !) dans pas longtemps. Et si ils n’ont pas le temps de le faire, il est possible que je m’y colle. Ca me fait mal aux yeux.

32 thoughts on “Crossbar, le futur des applications Web Python ?

  • Morgotth

    Très intéressant comme projet. Même si comme tu le dis l’API est “dégueulasse” et qu’en plus, ils se basent sur Twisted (qui semble en perte de vitesse et beaucoup critiqué), ça fait beaucoup de boulets pour un projet jeune.

    Mais comment as tu découvert ce projet ? Si même HackerNews n’en a jamais parlé (à ma connaissance), c’est encore assez confidentiel comme projet.

  • Sam Post author

    Twisted est un outil incroyable : le code source est fantastique, ses features sont monstreuses (serveur SSH, FTP, SMTP…), c’est super performant (asynchrone oblige), et tout ça dans 2 Mo. Mais c’est moche, mon dieu que c’est moche.

    Je découvre ce genre de projet en raclant le fond du Web :) Je passe une heure par jour à faire une veille techno. Et pour être honnête, j’attends un projet de ce genre depuis très très longtemps.

  • Sylvain Hellegouarch

    # Bon, on se décide, soit on fait une classe avec des noms
    # de méthode conventionnés, soit on met des décorateurs,
    # mais pas les deux, pitié !

    Ce serait sympa d’expliquer ce que tu veux dire ici… c’est pas clair et je ne voie pas le soucis d’avoir des décorateurs sur des méthodes.

  • Sam Post author

    Le décorateur ici est là pour permettre à la method d’utiliser des defered avec yield, et donc de coder avec une style de programmation synchrone, malgré le fait que ce n’est pas la cas.

    Maintenant, cette syntaxe est devenu le standard, donc on utilise toujours inlineCallback. Or, le nom des méthodes est conventionné, donc on sait ce qu’on va utiliser comme méthode pour faire des appels asynchrones.

    A moins de vouloir faire du très bas niveau pour garder la flexibilité, il est sain de vouloir soit :

    – implicitement appelé inlineCallback sur les méthodes où on va de toute façon le faire.
    – virer le concept de classe et utiliser le decorateur comme dans l’exemple de l’api flaskesque pour faire le lien entre les callback.

    Faire les deux, c’est mettre du beurre, et de l’huile, ça glisse bien, mais c’est lourd.

  • Stéphane

    Web Application Message Protocole -> Web Application Message Protocol (pas le “e” à la fin)

    COBRA -> CORBA

  • Sam Post author

    @Stéphane : merci.

    @keiser1080: parce que toute la communauté est passé à sockjs et que socket.io a vu son dev ralentir considérablement.

  • Topic

    Remarque qui n’a rien à voir mais c’est parce que je le fais souvent aussi, peut être dû au fait de faire des if toute la journée : les si il(s) –> s’il(s).

  • Francis

    Côté RPC il y a un projet sympa sorti par dotCloud (la boîte derrière Docker) : ZeroRPC (utilisé en interne). Basé sur ZeroMQ, Gevent et MessagePack.

    Donc il y a pas le côté client js pour le browser mais par contre pour du RPC entre workers c’est top et l’API est propre.
    Pour reprendre le Hello World de leur site, coté serveur :

    import zerorpc
     
    class HelloRPC(object):
        def hello(self, name):
            return "Hello, %s" % name
     
    s = zerorpc.Server(HelloRPC())
    s.bind("tcp://0.0.0.0:4242")
    s.run()

    Côté client :

    import zerorpc
     
    c = zerorpc.Client()
    c.connect("tcp://127.0.0.1:4242")
    print c.hello("RPC")

    Ils avaient lancé aussi un stack.io qui se rapproche plus d’Autobahn avec le support JS (navigateur et node) et Python, mais il n’y a plus de commit depuis 1 an sur le projet.

  • Sam Post author

    Oui, non seulement y a pas de support browser, mais de surcroit, il n’y a pas de support dans d’autres langage que Python et nodejs. Du coup on perd le côté universel.

  • G-rom

    Bon j’imagine que j’ai mérité le tampon de boulet…

    Je viens de voir que tu utilises Autobahn dans ton exemple de la partie serveur. Je pensais que c’était 2 projets distinct, j’ai du louper un truc.

  • kontre

    Punaise, le dev web c’est vraiment pas mon monde, je pige rien à cet article (enfin, pas grand chose). Mais bon, vous avez l’air content alors ça doit être bien !

    • Sam Post author

      @kontre: j’ai modifié l’article pour mettre des schémas. J’espère que je rends le truc plus clair, car si mon article d’explication explique rien du tout, j’ai pas rempli ma mission.

  • Sam Post author

    @G-rom: autobahn c’est un client, crossbar c’est un serveur.

  • PocketTiger

    Hey dit @Sam, il se passe quoi quand :
    Deux services déclarent une méthode avec le même nom ?
    Deux méthodes se rappel entre elles à l’infini ? (il y à une gestion de la profondeur de rappel ?)
    On demande une méthode qui n’existe pas ?
    On demande une méthode qui existe mais dont le service n’existe plus ?
    On ne passe pas le bon nombre d’arguments ?

    Peut-ton demander explicitement la méthode X du service Y et pas du service Z ?
    N’y à t-il pas un risque que des services étrangers se plug sur ton serveur pour faire un bon gros MITM ?
    Dans ton exemple de login, es ce que je ne peut pas me substituer à ton service d’identification avec un simple script GreaseMonkey ?

    J’admire l’idée et je remercie pour l’explication, mais sa sent le gaz ici.

  • kontre

    Merci pour les schémas, ça aide. Je crois que le problème n’est pas au niveau de l’explication, mais des pré-requis. Il y a plein de termes dont je n’ai jamais entendu parler ou dont de n’ai aucune idée de la signification ou de l’utilité, genre AMQP, RabbitMQ, websocket, SockJS, MeteorJS, le bon vieux CRUD, ZeroMQ…

    Ceci dit j’arrive à comprendre le fonctionnement général du truc, je vois que c’est différent du modèle simple client-serveur, donc de ce côté là c’est mission réussie.

  • foxmask

    @kontre : le schema s’est vu concis mais il en aurait peut-etre fallu un qui detaille au moins où se trouve la machinerie de gestion de files d’attente (dont c’est le job de ZeroMQ/AMQP/RAbbitMQ). Dans le 1ier schema si je ne dis pas de sottises, ces gestionnaires se trouvent dans les “fleches” qui relient chaque client mais la partie de la fleche qui est dans le orange “Routeur WAMP”. Ces gestionnaires sont sur le serveur (routeur wamp) et non le client.

  • G-rom

    @Sam : Autobahn fait aussi serveur.

    Apparemment c’est plus un framework avec un exemple de router simple, là où Crossbar est censé être une lib au-dessus de Autobahn avec une implémentation plus haut niveau et plus complète.

    Mais leur doc n’est pas très clair entre tout ça.

  • keiser1080

    dommage pour socket.io
    je sais pas si j’ai dejà partagé avec vous
    video
    regarder la video jusqu’au bout ça vaut vraiment la peine.
    Et le link vers la lib.

    gevent-socketio

    Alexendre Lebourget est un génie, excellent pédagogue sa lib est super simple à utilisé et de plus ça doc et claire.

    Meme moi je comprend :)

    Le seul truc que je comprend pas avec tout ce qui est ajax & web-socket c’est l’authentification.
    Je trouve pas d’exemple claire, ça donne l’impression que ces technologie sont faite pour les données ouverte.
    si vous avez un bon exemple je suis preneur.

  • Sam Post author

    @PocketTiger

    Les problématiques que tu soulèves sont les mêmes qu’on a à gérer avec n’importe quel service Internet, y compris les sites Web, depuis que ça existe.

    Hey dit @Sam, il se passe quoi quand :
    Deux services déclarent une méthode avec le même nom ?
    Deux méthodes se rappel entre elles à l’infini ? (il y à une gestion de la profondeur de rappel ?)
    On demande une méthode qui n’existe pas ?
    On demande une méthode qui existe mais dont le service n’existe plus ?
    On ne passe pas le bon nombre d’arguments ?

    Peut-ton demander explicitement la méthode X du service Y et pas du service Z ?

    Si deux services déclarent une méthode du même nom dans un routing, il y a de nombreuses politiques possibles (erreur explicite, duplication, etc), mais le plus utilisé est simplement que le dernier gagne. Je n’ai pas vérifié mais je pense que c’est ce qui se passe dans crossbar.

    Dans tous les cas, les exemples de noms donnés sont des exemples simplifiés. Dans la vie réel, toute application Internet utilise des namespaces pour éviter cela. Le plus connu étant l’URL (qui n’est PAS une représentation de dossiers comme l’a fait croire PHP), et on utilise parfois des URL avec WAMP pour les nom de ses événements. Néanmoins, la convention la plus courante est d’utiliser une syntaxe à base de “.”, par exemple : “auth.user.login”, “auth.staff.login”, “api.user.login” au lieu de juste “login” . Le prefixe du namespace étant généralement configurable par service. Du coup si on veut demander la méthode d’un service X au lieu du Z, ils auront un namespace différent. Il peut y avoir des collisions de namespace, comme il peut y avoir deux libs python qui ont le même nom dans le monde. On y peut rien, mais ce n’est pas catastrophique non plus, puisque ça se change facilement.

    Pour l’appel de méthodes qui s’appelle à l’infini, il n’y a pas plus de gestion qu’avec HTTP, SOAP, XMLRPC… Si tu as deux vues qui s’appelle avec des méthodes GET respectivement, tu as une boucle. C’est pareil ici. WAMP n’est PAS un truc magique, il a les mêmes propriétés que les technologies qu’on utilise déjà.

    Si on appelle une méthode qui existe déjà ou qu’on passe pas le bon nombre d’argument, ça lève une exception. Si la fonction déclenche une erreur, l’erreur est propagée au client et ça lève une exception, comme une fonction normale.

    N’y à t-il pas un risque que des services étrangers se plug sur ton serveur pour faire un bon gros MITM ?
    Dans ton exemple de login, es ce que je ne peut pas me substituer à ton service d’identification avec un simple script GreaseMonkey ?

    Pour le MITM, pas plus ou moins qu’avec HTTP. C’est du websocket, ça utilise le même réseau, avec les mêmes propriétés. La sécurité n’est pas lié à WAMP, mais à ton architecture. Si tu veux éviter un MITM, il te faut un certificat SSL, comme avec un site web https, et un mécanisme d’authentification (généralement on utilise un bon vieux token à la Oauth).

    Ta dernière question est très intéressante et je suis surpris que je n’ai pas plus de personne qui l’ait posé : si tout le monde peut enregistrer une fonction, qu’est-ce qui m’empêche d’écraser une fonction du serveur ?

    La réponse est qu’il existe une fonctionnalitéé de sécurité que je n’ai pas montré ici car l’article est déjà très long, qui s’appelle les realms. Un reamls c’est un peu comme un groupe unix, c’est à dire un namespace de sécurité. Quand tu te connectes à un serveur WAMP, tu te connectes dans le cadre d’un realm, déterminé par ton point d’entrée, avec différentes permissions.

    J’admire l’idée et je remercie pour l’explication, mais sa sent le gaz ici.

    Techniquement le gaz n’a aucune odeur, c’est un additif ajouté par les vendeurs de gaz pour permettre aux gens se détecter les fuites. Donc vraiment, tu sens l’additif du gaz.

    @foxmask : il n’y a pas de file d’attente. Crossbar est bien celui qui s’occupe du dispatch les messages, mais il diffuse tous les messages au fur et à mesure qu’il les reçoit. Le routing se fait au travers du nom des événements PUB/SUB et avec le nom des fonctions RPC, mais il n’y a pas de concept de file, et tu ne peux pas compter sur un ordre d’arriver des paquets. Comme avec NodeJS, tout est asynchrone, donc tout fonctionne par callback. Ton callback récupère l’info quand elle arrive, et tu n’as pas ton mot à dire sur le quand.

    @G-rom “leur doc n’est pas très claire” est un doux euphémisme. Oui autobahn contient un petit serveur WAMP 1, utile pour faire des petit service standalone. Crossbar lui, est un serveur plus robuste, qui gère aussi WAMP 2, et en prime permet de gérer des processus. Il va aussi bientôt se voir doter de la possibilité de connecter plusieurs noeuds crossbar pour le load balancing.

    @keiser1080: la raison pour laquelle tu ne trouves pas d’exemple d’authentification (et de gestion de permission, session, etc), c’est que c’est un problème difficile, et le gros point noir de ces applications. Les enthousiastes ont tendance à oublier que tout n’est pas rose. La solution la plus souvent utilisée est un token. Mais ça veut dire, dans les applications mixtes, qu’il faut que tu joues fair play avec l’auth par cookie également. Bref, ce que ne te disent pas les prophètes tu temps réel, c’est qu’ils passent leur temps à recoder des trucs basiques qui marchaient très bien avant comme l’auth, les formulaires, etc.

  • keiser1080

    C’est quand même dommage que des gents brillant qui ponde des trucs comme Crossbar ne pense pas à des choses aussi basique.
    Dans 99% des cas les utilisateur auront besoin d’un system d’authentification + gestion des rôles. Et les novices (et lucide) comme moi sont forcé d’utiliser autre chose.
    Tu me diras que n’importe qui peux développez soit même un systeme basique d’authentification, mais quand on vois des boite comme sony et meme la nsa se faire hacker ou encore dernièrement la faille openssl, qui peux prendre le risque de bricoler à part si c’est pour faire le site de sa grand maman et encore.

    Tu pense quoi de gevent-socketio?
    Et je suis curieux de savoir si tu connaissait ?

  • Sam Post author

    @keiser1080 : c’est pas qu’ils n’y pensent pas, c’est que le projet est jeune, pour l’instant ils fabriquent leur solution pour chaque projet histoire de voir ce qui marche le mieux, justement pour proposer à la fin la solution la plus adéquate. Comme tu l’as dit, c’est un problème compliqué, tu peux pas juste “ajouter la feature” et hop, c’est réglé. MeteorJs a eu exactement le même problème, et ils sont aller jusqu’à fabriquer un système qui envoie le contenu du cookie d’authentification via websocket, afin de concilier les deux mondes.

    Je connais gevent depuis longtemps. C’est une bonne techno, mais elle a plusieurs problèmes :

    – oblige à compiler une extension C. Pour le moment autobahn aussi, mais ils vont corriger ça.
    – event loop implicite qui monkey patch la lib standard. Parfois ça créé des bugs, tu sais pas d’où ça vient. Et parfois, tu écris du code bloquant sans te rendre compte que tu le fais dans une partie asynchrone.
    – pas d’utilisation du multi-coeurs.
    – pas de spécification de protocole, tu réinvente la roue à chaque fois, et pour la communication entre plusieurs systèmes différents, il n’y a rien de prévu.

    Encore une fois, gevent est une bonne lib, simplement elle est limitée à un groupe d’usages bien particuliers. On pourrait néanmoins tout à fait on pourrait même créer un client / server WAMP au dessus de gevent, d’ailleurs je te fiche mon billet que si WAMP devient populaire, ce sera fait.

  • foxmask

    je trouve ce projet super excitant du point de vue temps réel, et j’aimerai bien tenter une version de mon Trigger Happy avec, mais je ne sais pas par quel bout le prendre :) Vais rerererere-lire les wiki

  • keiser1080

    – oblige à compiler une extension C. => oui c’est basé sur libev (http://software.schmorp.de/pkg/libev.html)
    – event loop implicite qui monkey patch la lib standard. Parfois ça créé des bugs, tu sais pas d’où ça vient. Et parfois, tu écris du code bloquant sans te rendre compte que tu le fais dans une partie asynchrone. => en effet ça m’est arriver
    – pas d’utilisation du multi-coeurs. => ça c’est vraiment dommage y a t ‘il en python une lib équivalente (aussi simple d’utilisation) qui permet travailler sur plusieur coeur.
    -la communication entre plusieurs systèmes différents, il n’y a rien de prévu. => c’est dommage mais je pense pas que c’est leur objectif

    En fait je demandais ton avis (et celui des autre) sur https://github.com/abourget/gevent-socketio

    presentation:
    gevent-socketio is a Python implementation of the Socket.IO protocol, developed originally for Node.js by LearnBoost and then ported to other languages. Socket.IO enables real-time web communications between a browser and a server, using a WebSocket-like API. One aim of this project is to provide a single gevent-based API that works across the different WSGI-based web frameworks out there (Pyramid, Pylons, Flask, web2py, Django, etc…). Only ~3 lines of code are required to tie-in gevent-socketio in your framework. Note: you need to use the gevent python WSGI server to use gevent-socketio.

    dependence: gevent & gevent-websocket

  • Sam Post author

    gevent-socketio, c’était pratique au moment de sa sortie. Maintenant avec crossbar + autobahn, ça va devenir obsolète je pense.

  • Tobias Oberstein

    Hi,

    my name is Tobias – I started Crossbar (and Autobahn / WAMP).

    First: major thanks to Sam for writing this post. It’s an incredible refreshing look at all this stuff – me, I no longer have that. And I think Sam is right with the issues raised (and iterated by others in the comments).

    Stuff isn’t explained. The docs suck. The first user experience sucks. Well, I agree:( Thing is: day is 24h, and it’s a big scope / much work.

    FWIW, I revamped the project description (“wtf is this stuff?”) and added a quick start.

    Anyway, I am even more happy that Sam hasn’t stopped with critique, but is now contributing code and more. So I’d also like to take the chance to invite you!!

    Ah, btw, sorry for English – I missed French at school (took Latin, well, a mistake;) So hopefully Google Translate did a decent job ..

    Cheers,
    Tobias

  • MrPouet

    J’etais tranquillement sur le reddit de python quand tout d’un coup je vois un article sur crossbar en top 3, je me dit tiens Sam serait content, je clic et je me dit tiens une impression de réchauffé quand tout d’un coup :
    “Guest post by Sam from Sam & Max. Original (French) version here.”
    vous me suivez c’est ça ?

  • Garrigos adrian

    C’est un super article sur le devenir des webapps de demain. Ce que trouve génial dans ce projet, c’est l’universalité des données au service d’une même application. Et je dis bien une application car tout passera Demain par les webapps en push qui pourront nourrir les apps clients hétérogènes (C++, python, NodeJS, android). Tu imagines le truc pour les entreprises! L’unification logicielle et le gain de temps… En fait, ce truc c’est l’anneau qui les unie tous! (CRM, web, facturation, stocks) :-)

    Mais j’ai une réserve qd même, parce que ce principe d’universalité des données me fait furieusement penser à Docker et au Container, appliquée non plus au serveur web, mais aux webapps. Et si Docker s’est lancé là dedans, il faudra comparer le “choix” des 2 solutions.

    En tous cas super travail! Bravo, je te donne un 4X ce qui ne m’est encore jamais arrivé ;-) et je rtw, je g+, je Fb (euh, non ça va pas leur plaire…).

  • Sam Post author

    En fait docker et crossbar sont deux solution complémentaire, et il est très avantageux d’avoir une architecture orientée microservices avec des images dockers qui hébergent les clients crossbar, qui communiquent entre eux via wamp.

Comments are closed.

Des questions Python sans rapport avec l'article ? Posez-les sur IndexError.