Je sais, je sais, je vous fais chier avec crossbar et autobahn.
Mais ça me tue de ne pas voir plus de monde exploiter cette techno.
Pendant que Max fait la sieste, j’ai pris mon stylo et j’ai fait la liste des besoins d’une app Web actuelle. Quels sont les composants qu’on utilise presque systématiquement, mais en agrégeant divers bouts de trucs à droite et à gauche ?
Ensuite j’ai regardé les possibilités des outils WAMP :
- PUB/SUB et RPC.
- Asynchrone.
- Gestionnaire de process intégré.
- Serveur stand alone qui n’a pas besoin d’un proxy pour être en prod.
M’inspirant de cela, et du travail que je suis en train de faire avec l’équipe de Tavendo pour faire une API flaskesque pour autobahn, j’ai prototypé une API d’un framework Web qu’on pourrait coder au dessus de cette techno.
Voilà ce que ça donne…
Une API qui mélange flask et nodejs pour le Web
app = Application('YourProjectName') # Envoyer et recevoir des requêtes HTTP @app.http.post(r'/form') def _(req, res): res.json({'data': 'pouet'}) @app.http.get(r'/user/:id/') def _(req, res): res.render('index.html', {'data': 'pouet'}) # Servir des fichiers statiques @app.http.serve('uri', '/path/to/dir', [allow_index]) app.run() |
Comme c’est asynchrone, on a de très bonnes perfs. Comme c’est basé sur Twisted, on a pas besoin d’un serveur wsgi (gunicorn, uwsgi, etc) ni d’un proxy (nginx) devant. On peut le mettre en prod tel quel.
Parti de ce principe, on peut ajouter la gestion du PUB/SUB et du RPC pour WAMP :
# Callback attendant l'événement @app.wamp.event('auth.signedin') def _(ctx, a, b, c): pass # déclenchement de l'événément app.wamp.pub('auth.signedin') # Déclaration du fonnction appelable à distance @app.wamp.remote('auth.signin') def _(ctx, a, b, c): pass # appel de la fonnction app.wamp.call('auth.signin') |
On est souvent perdu quand on fait de l’asynchrone pour la première fois avec Python car on ne sait pas comment lancer du code après .run()
. On peut régler la question proposant des hooks pour les instants clés de l’app.
# Callback à lancer quand l'app est prête @app.on('app.ready') def _(ctx, args): pass # Signalement que l'app est prête (fait automatiquement en interne # pour les moments les plus importants) app.emit('app.ready') |
Et tant qu’on y est, puisqu’on a une event loop, profitons en pour proposer du CRON intégré à l’app. C’est moins chiant à déployer qu’un script CRON, c’est cross plateforme, et on a accès facilement à toute sa stack.
# Lancer du code tous les x temps ou a une date précise @app.cron(every=seconds) @app.cron(every=timedelta, overlap=False) @app.cron(hour=7, minute=30, day_of_week=1) @app.cron(when=datetime) def _(ctx, args): pass |
Pourquoi s’arrêter là ? Event loop + message passing + safe queues + workers = tasks queues !
# Créer une file d'attente queue = @app.queue('name', [workers], [result_backend]) # Callback appelé par un worker quand il depop ce # message dans la file @queue.task('encode.video') def _(ctx, data): pass # Envoie d'une tache dans la queu queue.append('encode.video', data) |
Comme on utilise Twisted, on a accès à une chiée de protocoles, et on peut aussi créer les siens. On peut donc imaginer un système de plugins qui rajoute des protocoles supportés :
app = Application('YourProjectName') app.plug('lib.ajoutant.sms', [namespace]) |
Si on en a beaucoup et que le namespace nous convient :
app = Application('YourProjectName', plugins=('lib1', 'lib2', 'etc')) |
Exemples de plugins possibles :
# Recevoir et envoyer des SMS (via un service type twilio, une gateway kannel ou # un modem physique) @app.sms.receive(r'LOVE \w+ \w+') def _(ctx, args): pass app.sms.send('test', [contact]) # Envoyer et recevoir des emails (via un server SMTP ou IMAP) @app.email.receive(src=r'.*@sametmax.com', dest=r'spam.*@*.') def _(ctx, args): pass app.email.send('test', [contact, title, attachments]) # techniquement n'importe quel service de message pour lequel on peut écrire # un backend @app.tweet.receive(r'Chat') @app.fb.receive(r'Like') @app.instagram.receive(r'Bouffe') @app.irc.message(r'dtc') def _(ctx, args): pass |
Le problème des apps centrées sur un objet, c’est qu’elles ont souvent un design monolithique. Ce n’est pas un problème du concept d’app, c’est juste que les auteurs ont pensé “point d’entrée”, et pas “élément composable”.
Si besoin, on doit pouvoir composer une app via plusieurs sous-app :
app = Application() app.embed('autre.app') |
ou
app = Application(embed=['app1', 'app2', 'app3']) |
Il faut des hooks pour overrider la configuration, mais vous avez compris le principe.
Un autre problème avec les plateformes comme NodeJS, c’est qu’il est difficile d’utiliser plusieurs coeurs. C’est une des raisons du succès de Go.
Or, Crossbar encourage la division en plusieurs process qui communiquent entre eux (un peu comme les channels). Créons aussi une API pour ça :
p1 = app.process() p2 = app.process() # Déclarer et appeler une procédure dans process 1 @p1.wamp.remote('auth.signin') def _(ctx, args): pass # Déclarer et appeler une procédure dans process 2 @p2.wamp.event('auth.signedin') def _(ctx, args): pass |
Ainsi on profite enfin de plusieurs CPU. La même chose en plus facile à changer:
# Déclarer et appeler une procédure @app.wamp.remote('auth.signin') def _(ctx, args): pass # Déclarer et appeler une procédure @app.wamp.event('auth.signedin') def _(ctx, args): pass app.processes({ 1: ['wamp.remote:auth.signin'] 2: ['wamp.event:auth.signedin'] }) |
En bonus, on fait la nique au GIL.
Mieux, on peut bouger ses process sur plusieurs machines :
Machine 1 (routeur):
router = Application(endpoint="0.0.0.0:8080") router.run() |
Machine 2 (authentification):
# IP du router auth = Application('auth', connect_to="182.64.1.15:8080") # Nommage automatique en fonction du nom de la fonction # et de l'app, avec possibilité d'annuler ou overrider le prefix. # Ici du coup la fonction s'appellera en RPC via 'auth.signin' @auth.wamp.remote() def signin(ctx, args): pass auth.run() |
Machine 3 (API REST):
web = Application('site', connect_to="182.64.1.15:8080") @web.http.post(r'api/auth/') def _(req, res): user = yield res.wamp.call('auth.signin', req.POST['username'], req.POST['password'])* if user user = yield res.wamp.pub('auth.signedin', user.userid) res.json({'token': user.token}) else: res.json({'error': 'nope'}) @web.http.get(r'api/stuff/') def _(req, res): res.json(get_stuff()) @web.http.serve('uri', '/path/to/dir', [allow_index]) web.run() |
Et vous savez le plus beau dans tout ça ? En Python on a plein de libs qui sont encore bloquantes. En théorie on ne peut pas les utiliser dans les apps asynchrones. Quand on a toute sa logique métiers dans des classes d’ORM, c’est balot. Mais pas ici ! On met un process avec tous ces appels bloquants, et on les appelle depuis des process non bloquant en RPC de manière asynchrone. Pif, paf, pouf, problème isolé.
Après, libre à son imagination de rajouter des fonctionnalités de confort…
Callback qui sera appelé seulement x fois :
# Déclarer et appeler une procédure @p1.wamp.event('auth.signedin', options={'limit_calls': x} ) def _(ctx, args): pass |
Raccourcis pour les opérations courantes :
# Recevoir et envoyer un événement @app.sub('auth.signin') def _(ctx, *args): # ctx.pub @app.pub('auth.signedin') # Déclarer et appeler une procédure @app.proc('auth.signedin') def _(ctx, args): # ctx.call app.rpc() |
Comme je vous l’avais expliqué, crossbar peut gérer le cycle de vie de services externes à votre application au démarrage. Autant exposer cette API programativement :
@app.service(['/urs/bin/nodejs', 'script.js'], [user], [group]) |
.run()
, c’est cool, mais si on veut changer des options via la ligne de commande, faut se taper tout le boulot alors que ça pourrait très bien se générer automatiquement :
@app.cmd_run() |
Et si vous faites : python sites.py --debug=true --endpoint=0.0.0.0:5252
, ça le prend automatiquement en compte. Y a pas de raison de se faire chier.
En parlant de générer automatiquement des trucs, le fichiers de configs pour les services externes sur lesquels on peut avoir envie de brancher notre app, c’est toujours galère. Autant fournir un exemple de base qui est sûr de toujours marcher, généré avec les paramètres de notre app :
python site.py template centos:nginx
python site.py template ubuntu:upstart
python site.py template bsd:systemd # :D |
On peut partir très loin dans le délire “battery included”. Typiquement, on peut fournir des services externes nous même puisque crossbar nous le propose, et coder des versions moins bien, mais compatibles (et suffisantes pour les petits sites), de projets toujours utilses :
- cache (compatible redis)
- live settings (compatible etcd) mais avec en prime un event wamp propagé à chaque
- build (compatible, heu, j’en sais rien) qui s’occupe en tâche de fond de surveiller le >système de fichier et lancer les compilations, les minifications, les copies, les tests unittaires, etc. logging centralisé (compatible sentry).
- Un bridge WAMP/REST qui permet d’envoyer et recevoir des events WAMP sur votre app Django ou flask en utilisant HTTP.
changement de valeur
On plug tout ça a une admin Web.
J’espère que je vous ai donné maintenant l’envie de vous plonger un peu plus dans cette techno, et peut être coder quelque chose avec.
Il n’y a plus d’excuses pour ne pas avoir de framework web next gen, ultime de la mort qui tue en Python. A part le fait qu’on soit des feignasses.
Ah, merde, on est foutus.
Par votre faute, à force de vous lire et de galérer pour comprendre la moitié des propos (très intelligents, et agréables à suivre) j’ai fini par céder : je vais flirter avec Python.
Je me suis inscrit sur des MOOC qui impliquent de programmer en python (faut qu’on me tienne la main pour que j’apprenne correctement).
JE VOUS HAIS, JE VOUS HAIS, JE VOUS HAIS.
Tu comptes développer ce framework ?
J’ai pas le temps de tirer ça moi même, mais je me ferais un plaisir d’aider :)
Je garde ton article sous la main, car j’en aurai besoin dans ma future application django …
Max : y’a-t-il une possibilité que tu mettes aussi tes articles en PDF pour une lecture hors ligne ? (simplement un plugin à wordpress qui convertit le post en PDF … )
Sam pardon ^^
@e-jambon: laisse la haine t’envahir.
@C4: j’en ai envie, mais j’ai peur de me lancer, réaliser que la tâche est trop grande pour moi, abandonner, et décevoir tout le monde.
@said : tu peux télécharger un dump du site hors ligne ici :
https://github.com/sametmax/miroir-du-blog
Sinon, pour avoir un article individuellement, je t’avoues que j’ai la flemme de rajouter encore un plugin. Je vais essayer de rajouter un truc genre http://pdfcrowd.com/save-to-pdf/ que tu peux utiliser en attendant.
Bon, voilà, téléchargement des pages en PDF ajouté.
Salut, un bon dimanche partagé entre le tennis, et des séries, et là paf arrive ce billet.
Dans la veine de e-jambon, ca m’intéresse beaucoup comme je l’ai dit par ici aussi, j’ai pigé, mais … ça fait beaucoup à appréhender pour arriver au début d’une poussière d’une ligne de code.
Par où commencer zatize ze question.
Il faut déjà arriver à maitriser l’archi de corssbar pour savoir comment articuler son projet.
Ce qui m’emmerde dans tout ça c’est que j’ai l’impression de voir une techno façon “poupée russe” (comme java), crossbar abstrait wamp, qui abstrait autobahn, qui abstrait twisted, etc… Quand on ne connait aucune de ces briques, on part de TRES TRES loin et on fini par se dire, “ok j’ai pas le niveau pour ce truc qui ressemble à une tuerie, donc je vais rester avec mon truc monolithique (moche?) mais simple que je connais/maitrise”
Donc pour revenir sur :
yena qui aimeraient mais ne saisissent pas tout quand bien même ils voudraient.
Peut-être que plus de concret (des exemples de bout de code mettant en branle tout la tuyauterie qu’on pourrait tester ourselves) et moins d’abstrait aiderait. Comme c’est assez récent tout ça, trouver des projets existants pour parcourir le code, reste compliqué.
@foxmask: je suis tout à fait d’accord avec toi foxmask. Mais c’était pareil avant Django, et il y a eu un mec qui l’a créé. Et pareil avant Rails, et nodejs, etc. Il y des personnes qui ont le niveau pour ça, et qui peuvent faire quelque chose de crossbar. Les gens comme Tarek Ziadé, Alex Martelli, Alex Gaynor, Gael Pasgrimaud, Kenneth Reitz, Jacob Kaplan Moss, Ian Bicking, Gael Varoquaux, Tim Peters, Charles Leifer ou Jessica McKellar ont le niveau.
On a les talents, c’est dommage de voir MeteoJs évoluer et n’avoir rien de notre côté.
Je ne vais pas te jeter la pierre, moi-même je suis intimidé par la complexité du truc. J’ai passé des heures et des heures à lire de la doc sur twisted, puis à lire leur code source, pour ne serait-ce que réussi à commencer à travailler sur leur projet.
Car en l’état ce n’est pas utilisable. Ce sont des briques, robustes, versatiles, mais il faut quelque chose de plus haut niveau pour attirer du public de dev d’app.
Au fait, merci beaucoup pour les corrections. Je ne le dirais jamais assez, c’est vraiment super sympa.
gageons que ca se prepare en louzedé
ou que ces talents nous pondent des app dont on puisse s’inspirer, sur lesquelles s’appuyer.
c’est une modeste contribution compte tenu des vôtres.;)
@e-jambon post les link vers les moc python. Je viens de terminer un moc python sur coursea.org
@sam tu vas crée un framework ou ce sont des fantasmes, le framework idéal ?
En tout cas la description ressemble un peux à celle de web2py.
voir sur cette page http://web2py.com/init/default/what
& ici http://web2py.com/books/default/chapter/29/00/preface
Pour que les dev accroche à crossbar il faudrait un tuto un peux comme celui ci
http://killer-web-development.com/
Le gars à une super approche bien que le tuto n’est pas fort détaillé,
je trouve l’approche excellente.
C’est une mise en contexte fictive, le gars ce présente comme étant le ceo d’une boite qui souhaite une application web, il donne un cahier des charge et ensuite passe en revue toute les étapes de la création de l’application en parlent des outils utilisé ect…
Quand un débutant à terminé il à déjà une vue d’ensemble et peux se débrouillé pour faire une petite application de A à Z.
C’est très différent des tuto de Sam et Max.
Mais le concept pourrais être copier pour crossbar et même améliorer en mettant des shortcut vers vos différent article et tuto :)
actuellement quand je cherche quelque chose j’ai quatre source soit google les réponse stack overflow soit google les tuto dans les blog perso, mais si j’ai dejà lu ou vu sur sam et max ou dans les snippet de sebsauvage je commance par s&m et ou sebsauvage.
Sur s&m c’est pas toujours évident de retrouver l’article.
je suis pret à faire l’effort de migrer de web2py vers une solution genre crossbar si c’est aussi simple et documenté que web2py.
Pas parce web2py mauvais, mais parce qu’il conviens pas à l’usage que j’en fait.
Je travail avec des développeur qui font principalement des outils shell en ruby et aussi un qui fait du perl et java ect…
Je suis censé faire un frontend web qui appel leur application via des appel rpc via rabbit mq sauf que rabbit mq c’est juste pour faire transité des message la couche rpc est à faire sois même. J’ai fait un client rabbit mq qui marche bien mais le serveur est un peux foireux et pareil pour mes collègues.
Les script shell prenne parfois jusqu’à plus d’une minute pour répondre et du faite que web2py embarque Rocket qui n’est pas fait pour de l’async, ça pause des problème de performance. De plus pas de possibilité d’utilisé plusieurs cœurs.
Donc j’ai deux options:
1. utiliser web2py pour tout ce qui est formulaire auth db ect…
Et faire des application independante que j’appel comme des webservice (rest, xml-rpc ou autre), et fabriquer un systeme qui utilise sockjs ou qq chose dans le genre qui vas se charger d’afficher des notification quand l’exécution sera terminé, ensuite l’utilisateur cliquera sur la notif pour voir le résultat (issue de la db)
2. passez à autre chose genre crossbar fait d’une multitude de brique pas nécessairement conçu pour marcher ensemble.
Et devoir tout apprendre :)
Dans le courant de la semaine on vas suivre ensemble une introduction à GO.
Go m’interresse mais pour le moment pas de framwork web complet, et je doute que au niveau de la productivité go puisse égalé python.
@Sam : et bien, si tu te lances un jour je serai content d’aider :)
Et merde.
‘faite chier. Des mois que je résiste mais bon, là, c’est le coup de grâce…
… ok je me met à Python
Ma libido, ma vie de couple et mon foie vous remercient.
@Sam : Vu le nombre de loustic qui viennent de se mettre a Python apres avoir lu ton post sur le framework ideal, tu n’as plus le choix : au boulot. tu voudrais pas decevoir toutes ces charmantes tete blondes, quand meme ?
@keiser1080:
Aucun framework n’est idéal. Ici c’est une démonstration de ce qui est faisable avec la techo. A ma connaissance, actuellement, aucune techno ne permet de créer un framework qui fasse tout ça. NodeJS ne peut pas faire de multi-coeur/multi-process facilement, le PUB/SUB et RPC se fait à la main de façon non standard. Erlang est juste un langage syntaxiquement très difficile, même avec Elexir par dessus, et Go manque cruellement de la quantité de libs Python à dispo.
Je pense que je suis techniquement capable de coder ce framework. Je ne suis pas certain que je sois humainement capable de le faire. On l’oublie souvent mais les auteurs des grands frameworks ne sont pas justes de gros geeks, se sont aussi des personnes extraordinnaires. Volonté, vision, continuité, capacité de gestion d’équipe, etc. Moi, rien que de voir le boulot, j’ai envie de faire la sieste.
Ce concept est tout de même un peu différent de Web2Py, dans le sens où il est très orienté asynchrone, et peu orienté interfaces graphiques. Ce n’est pas une mauvaise chose, la diversité est source d’innovation.
@C4: ok. N’appuie pas F5 toutes les 5 minutes pour voir si le projet à commencer quand même, hein.
@Erase: t’inquiète, l’alcool et le sexe se mélange très bien avec Python. Demande à Max, il vit en thailande et il boit comme un trou. Bon, par contre la vie de couple…
Attention Erase, ce framework n’existe pas, c’est un concept, hein.
@Teocali: j’aimerais bien t’y voir toi :) Rien qu’écrire le prototype de l’API sur papier, ça m’a pris une après midi.
Ceci dit, j’ai discuté avec Gordontesos, et on va peut être se voir cet automne pour sprinter un premier jet. Je ne fais pas de promesses, c’est un coup à faire un vaporware, mais ça coûte rien d’en parler.
J’ai pensé à 2 trucs en lisant l’article:
– Sam a bien trippé
– Bon courage pour la doc d’un truc comme ça !
@Sam : Merci de me rassurer sur ces points : je sais au moins maintenant que j’ai les trois pré requis hors technique (quoique…) pour me mettre sur Python.
J’avais bien compris la démarche conceptuelle et illustrative de l’article. Et c’est d’ailleurs ce qui intéresse le développeur Javascript que je suis : illustrations, principes et théorie avec un soupçons d’exemples.
Je vous enverrai une carte postale quand j’aurai atteins un niveau de compréhension suffisant pour justifier un voyage en Thaïlande :)
Il le fallait, cette nuit j’ai rêvé de crossbar.io… oui vraiment.
Je me suis réveillé en pensant à de l’asynchrone, des process, en me posant mille questions de savoir si c’était assez mûr pour passer en production ou pas.
Ton idée de framework m’intéresse énormément.
Et si tu lançais le projet ? Un Github et c’est parti ;)
Je suis sûr qu’il pourrait en ressortir quelque chose de vraiment bien.
Il faudrait quelqu’un puisse poser les bases du projet pour qu’on puisse contribuer.
Qui sait peut-être que tu pourras même rajouter ton nom à la liste de ceux que tu as cîté…
@keiser1080 : C’est le mooc de coursera justement : https://class.coursera.org/pythonlearn-002
J’ai d’autres trucs en cours (j’apprend à utiliser rails / cucumber là), et j’ai pas encore beaucoup de temps à consacrer à Python. C’est vraiment comme j’ai dis : pour flirter avec Python, j’en suis plus à apprendre la programmation depuis le temps… Chuis un vieux schnock moi, ça y’est ;)
@sam
c’est trop tard tu peux plus faire marche arrière!
Tu es condamné à nous pondre un framework asynchrone :)
Mais sans dec tu peux lancé un projet github sans t’engagé pour la continuité et peut être que des gents comme Tarek Ziadé, Alex Martelli, Alex Gaynor, Gael Pasgrimaud, Kenneth Reitz, Jacob Kaplan Moss, Ian Bicking, Gael Varoquaux, Tim Peters, Charles Leifer ou Jessica McKella vont accroché.
Et peux être que tu auras ta page wikipedia à cote de celle de Guido :)
@e-jambon
j’ai suivi https://class.coursera.org/interactivepython-004, si tu viens d’un autre langage c’est intéressent mais par contre si tu commence à 0 c’est faisable mais ça demande du travail.
les deux première semaine c’était assez ennuyeux mais ensuite ça grimpe en flèche coté python pour moi ça allais mais ça prend du temps et surtout il y a aussi des math vecteur ect… Et la je dois dire que je me souvenais de rien ;)
Message à caractère informatif
Un mot pour ceux qui seraient interessés :
crossbar.io ne fonctionne pas en 3.4.x alors que Autobahn oui.
Je me suis fait avoir en décortiquant le code et la doc de crossbario et en l'installant, comme les 2 projets sont du meme auteur je me suis dis autobahn l'étant (et utilisant selon la version de Python : twisted ou asyncio), crossbar.io ne peut que l'être. Mais non.
Donc j'ai pris ma plume pour en faire part à son auteur qui a leve l'ambiguïté dans son wiki.
Alors à quoi ca sert tout ça ? "alors à rien, c'était juste pour faire avancer le schmilblik" (c) coluche.
signé le travailleur de/dans l’ombre :P
Tu t’es auto tamponné ?
En tout cas c’est une info importante :
– crossbar sert généralement de routeur, donc on peut l’installer à part, le lancer, et l’oublier.
– autobahn sert généralement de client, donc pour écrire son app, et on peut le faire en 2.7 ou 3.4. Peut importe à quel crossbar il se connecte.
oui, je me suis tototamponné le coquillard (en ce moment ce que je dis tombe aux oubliettes un peu partout alors je me self-tamponne :)
c’est ce ticket github et la doc qui fait une précision sur l’utilisation d’autobahn
D’ailleurs c’est pas encore bon ; je viens de remettre une couche dans le ticket. Si on utilise crossbar avec python 3 c’est en python 3.3 ; or citer asyncio (apparue) en 3.4, va nous foutre la confusionnage. Donc autobahn|python + crossbar si obligatoirement Twisted. Sinon faut faire du Autobahn sans crossbar avec la 3.4 & asyncio.
bref ; on (bibi) n’est pas rendu :)
Non, c’est bien le truc qui est confusionant. Tu peux utiliser crossbar comme tu utiliserais apache, on s’en branle de sa version. Tu lance crossbar et il charge les services autobahn 3.4 sans problème.
Encore un truc pas clair, ça marchera jamais si ils font pas d’effort.
je vais aller au bout avec un test QQ la praline, alors je verrai
Bon alors soit :
1) le projet est trop jeune et la doc super short (genre on nous colle des exemples de python sans nous foutre les from x import y de rigueur; du coup pour tester on est “bien” mal barré)
j’ai le serveur crossbar qui est lancé pépère mais rien de ce que je fous comme code ne le contacte.
apres le “getting started” , comme suis borné avec python 3.4 ;) j’ai pris par exemple ces 2 scripts
Ensuite dans les scripts on nous site des ‘com.myapp.topic1′, ca ressemble a une arbo de dossiers ; mais on nous dit pas comment c’est sensé etre goalé : topic1 c’est un package ? com/myapp sont des dossiers ou pas ?
2) je n’ai (absolument) pas le niveau
Ca reste encore super flou – dommage – ca donne bien envie mais c’est frustrant d’en rester là – donc vais oublier qu’un tel truc existe en attendant que ca s’etoffe un pneu
Ouai je comprends complètement ce que tu dis. C’est clairement de la merde leur doc.
Je vais faire un tuto.