Trucs de geek

[Updated 2] Atom-PubSub module for ejabberd

Posted on juin 19, 2008

As requested, the Atom PubSub bridge

This module offersr an AtomPub interface to ejabberd PubSub data. Currently in two unfinished flavors, one for use with yaws embedded. One for use with ejabberd_http server

Howto

You need to have Yaws available. It will start in embedded mode, with the mod_yaws module (included). To build, edit the Makefile to match your erlang install and make Put the resulting beams in some place where ejabberd will find them.

Also you’ll need to set the BASEURL macro in atom_pubsub.erl to your webserver hostname.

You’ll also need to add the module to your ejabberd.cfg in the mmodules section:


{mod_yaws,[{logdir, "/tmp/"},
    {servers, [
    {"localhost", 5224, "/opt/var/yaws/www", [
     {dir_listing, true},
     {appmods, {"/atom",     atom_pubsub}}
     ]}
   ]}
  ]}

What you get

The AtomPub interface passes the Atom Protocol Exerciser (though some warnings remain).

It means that any AtomPub clients will be able to post to a specific node in your PubSub tree.

It also means that your PubSub tree will also be available as an AtomFeed.

Of course, each time an item is posted through AtomPub or PubSub on a node you are subscribed to, you’ll get the notification.

Can I have it with OpenFire and Epeios ?

That’s not possible. At some point, there’s no way around hitting directly the PubSub mnesia tables. So you can’t extract the code as a component.

Moreover, it only works with PubSub nodes derived from the default node type. (because of the mnesia tables stuff)

What’s next ?

I’ll update the code soon. A few of things I’d like to implement :

  • remove all calls to mnesia and work through mod_pubsub API.
  • add HEAD, etag and slug support (that’s a patch for ejabberd though)
  • remove that baseurl horrible macro
  • add node subscription through REST
  • as soon as ejabberd 2.1 is published remove dependency from yaws
  • add binary collections support

Mickaël Rémond from Process-One kindly offered to host atom-pubsub on the ejabberd_modules svn.


svn co https://svn.process-one.net/ejabberd-modules/atom_pubsub/trunk/

There’s a quick port to the ejabberd_http server at this location : You need to be running ejabberd 2.1 or current trunk to have it work.


svn co https://svn.process-one.net/ejabberd-modules/atom_pubsub/branches/ejabberd_http_branch/

Check out the README for installation.

Shoot your questions in the comment or via email (anything on this weblog domain goes to my inbox)

Serveur Jabber en ligne

Posted on juin 19, 2008

En fait, non. Je l’ai coupé. Je le remettrais un jour.

En attendant pour me joindre via XMPP : cstar chez ohmforce point com

(Celui d’Ohm Force, s’il tombe, je me fais taper sur les doigts).

CookieStore on Yaws

Posted on avril 10, 2008

I implemented a session cookie store, just like the one in Ruby on Rails 2.0.

Available with the same caveats : Session data is encoded in base64 and sent in the cookie with a SHA MAC of this data. This means that the user can see what’s inside, but will not be able to tamper with it.

Moreover session data should stay in small amount as the encoded and signed data may not exceed 4096 bytes.

This being said, that should give us Yaws clustering for free :) And no more sessions to expire, just set the cookie expiration date.

One small thing, make sure crypto is started.

session1.yaws has been rewritten to make use of this code.

Download here.

mini Auth CAS on Yaws

Posted on avril 03, 2008

Yaws, c’est le serveur web écrit en erlang, célèbre pour ce graphe qui montre comment Yaws met sa pâté à Apache.

Voici le client minimal permettant de s’authentifier sur un serveur CAS.

A sauver dans un fichier cas.yaws à mettre dans le /var/yaws (document root par défaut).

N’oubliez pas d’adapter CASHOST et SERVICE à votre configuration.

<erl>
-define(CASHOST, "http://localhost:8080/cas/").
-define(SERVICE, "service=http%3A%2F%2Flocalhost%3A5224%2Fcas.yaws").
-include_lib("xmerl/include/xmerl.hrl").

out(A)->
    H = A#arg.headers,
    C = H#headers.cookie,
    inets:start(),
    case yaws_api:find_cookie_val("casuser", C) of
        []->
            check_auth(A);  
        Cookie ->
            {ok, Username} = yaws_api:cookieval_to_opaque(Cookie),
            {html, "Authentified as "++Username}
    end.

check_auth(A)->  
    case queryvar(A,"ticket") of
        {ok, Ticket}->
            case verify_ticket(Ticket) of
                {ok, Username} ->
                    Cookie = yaws_api:new_cookie_session(Username),
                    CO = yaws_api:setcookie("casuser",Cookie,"/"),
                    [{html, "Authentified as "++Username}, CO];
                {error, Reason} ->
                    [{status, 403},{html, "Unauthorized : "++Reason}]
            end;
        undefined ->
            {redirect, ?CASHOST ++ "login?" ++ ?SERVICE }
        end.

verify_ticket(Ticket) ->
    inets:start(),
    Url =?CASHOST++"proxyValidate?"++?SERVICE++"&ticket="++Ticket,
    {ok, {_Status, _Headers, Body}} =
          http:request(Url),
    { Xml, _Rest } = xmerl_scan:string(Body),
    inets:stop(),
    case xmerl_xpath:string("//cas:user/text()",Xml) of
        [ #xmlText{value=Username} ] ->
            {ok, Username};
        [] ->
            {error, "invalid ticket " ++ Url}
        end.
</erl>

Tant qu’on est sur le sujet, il y a un article très intéressant sur le REST dans Yaws chez InfoQ.

Bonnes résolutions

Posted on janvier 04, 2008

Pour commencer, bonne année et meilleurs vœux pour cette nouvelle année !

Les résolutions professionnelles (les autres, je les garde pour moi)

Exercice obligatoire en cette période de l’année, une liste de résolutions. Avec un bilan en fin d’année 2008 (c’est ma première résolution, tiens, faire un bilan à la fin de l’année)

MangerVite sur mon iPhone

Peu de temps l’année passée pour fignoler MangerVite …

Toutefois une version iPhone me semble super indispensable, et j’ai envie de faire un peu de développement web pour cette plate-forme.

Parenthèse sur l’iPhone : pouvoir lire ses flux RSS dans le métro, priceless ! En bon utilisateur de NetNewsWire, j’apprécie l’intégration avec le version iPhone de NewsGator : mes flux sont synchronisés entre le téléphone et le MacBook Pro, et c’est bien pratique :)

XMPP

Je vais continuer à travailler dessus. Un client développé avec Jiggy ? (oui, toujours pour iPhone, il est vrai que je suis assez emballé par ce téléphone).

Je vais aussi, mais c’est plus difficile, continuer de convertir du OhmMan à Jabber, pour finir par me débarrasser d’IRC. (let’s kill this beast !)

J’ai installé un ejabberd sur un serveur d’ohmforce, avec authentification LDAP, et remplissage automatique du roster avec la liste des OhmMen. Il me reste des problèmes d’encodage à régler avec la passerelle IRC, mais vu que j’ai corrigé le problème sur cestari.info, y a pas de raisons :)

Sur le protocole, il faut que je bosse sur pubsub, vu que j’ai pas réussi à faire tourner quoique ce soit d’intéressant (les exemples XMPP4R ne tournent pas avec ejabberd pre-2.0.0)

Java

  • Evaluer GlassFish

  • Alfresco, en repository de documents avec workflow, multilingue et versionning, le tout accessible en ReST. Sexy beast. Il faut que je passe du temps dessus.

  • Grails et Groovy … je ne sais pas. J’étais assez emballé, jusqu’à ce que je m’y mette un peu. Le temps perdu par l’exécution des scripts (oui, il faut bien qu’elle démarre, cette JVM, et JRuby a le même problème) m’a bien refroidi, et je trouve la syntaxe de ruby tellement plus agréable …

Erlang

Faut que je repasse un peu de temps dessus, pour affiner mod_rpc, et l’utiliser :)

mod_rpc : Jabber-RPC within ejabberd

Posted on septembre 12, 2007

What is mod_rpc ?

mod_rpc is an ejabberd module which will handle rpc queries … in a modular way.

It is is easily extensible, and is designed to access the mnesia database from XMPP clients.

It plugs in the access control list to allow or prevent access to the rpc modules.

Installing mod_rpc

Download the mod_rpc.erl file and copy it over your ejabberd src/ directory. make ejabberd.

Do not restart yet, we have some configuration to do !

Using mod_rpc

Let’s say you want to publish two functions, echo and mult. The code would go as follow :

rpc_test.erl :


-module(rpc_test).
-export([handle/2]).

handle(_State, {call, echo, [A]}) -> {response, [A]};
handle(_State, {call, mult, [A, B]}) -> {response, [A*B]};

Copy into your ejabberd source directory and make.

Now you need to configure access to your functions. In

ejabberd.cfg


% 2 groups : admins, and the rest.
{access, rpc_admin, [{allow, admin}]}.
{access, rpc_all, [{allow, all}]}.
%...

%... in modules configuration
%...
  {mod_register,   [{access, register}]},
  {mod_rpc, [{access, [{rpc_test, rpc_admin}] }]}, % only admins can call echo and mult
  {mod_roster,     []},
%....

Now start ejabberd. (of course you could do the hot code stuff if you want)

From now on I have rpc_test@rpc.localhost answering to my rpc queries.

Let’s test from ruby using xmpp4r. Get the SVN version for jabber-RPC support.


require 'xmpp4r'
require 'xmpp4r/rpc/helper/client'
require 'xmpp4r/rpc/helper/server'
include Jabber
jid = JID::new('cstar@localhost') #this one is admin !
cl = Client::new(jid)
cl.connect
cl.auth("PASS")
rpc= RPC::Client.new(cl, 'rpc_test@rpc.localhost')
puts rpc.call("echo", 'Test string') # outputs Test string
puts rpc.call("mult", 2,4) # outputs ... 8

If you try with a non-admin user, you’d get

Jabber::AuthenticationFailure: not-authorized

About Groovy Jabber-RPC

I have been playing with it, and it does not work directly out of the box. The groovy lib will try to see if rpc_test@rpc.localhost is in the user roster.

Patching to making it work is quite simple ; In file xmlrpc-groovy/src/main/java/groovy/net/xmlrpc/JabberRPCServerProxy.java

Just replace : request.setTo(getId(connection.getRoster(), this.to)); (line 102)

with : request.setTo(this.to);

And the following will work :


import groovy.net.xmlrpc.*
import org.jivesoftware.smack.XMPPConnection

def clientConnection = new XMPPConnection("localhost")
clientConnection.connect()
clientConnection.login("cstar", "PASS") 
def serverProxy = new JabberRPCServerProxy(clientConnection, "rpc_test@rpc.localhost")
serverProxy.echo("test")
clientConnection.disconnect()

Necessary caveats

This is my first foray in developping a module in ejabberd, I still have to check how this actually scales. I only have one process handling all queries, which is not very concurrency oriented programming :)

Thanks

The guys from ejabberd, for making software really easy to use and extend ;)

Download :

mod_rpc.erl

Feedback

I really welcome enhancements and fixes (especially regarding the concurrency stuff!)

License

Don’t sue me, don’t remove copyright/name kind of license.

From ejabberd xml to xmerl and back - XEP-009

Posted on septembre 11, 2007

Those last days I was busing implementing an ejabberd module responding to Jabber-RPC (a.k.a XEP-009).

I used the xmlrpc module shipped with erlang to do the parsing. But that was tricky. xmlrpc uses xmerl for parsing and representing XML but ejabberd does not. It a simpler xml module handling the parsing.

To make a long story short, to get ejabberd’s xml understood by xmlrpc, here’s the thing :

% ....
xmlrpc_decode:payload(xmerl_ucs:to_utf8(
            xml:element_to_string(xml:get_subtag(SubEl, "methodCall")))),
% ....

SubEl being the sub_el field of an iq packet.

The complex part is that you need to call xmerl_ucs:to_utf8/1 else the module will crash.

For returning the resulting XML to the client, you need to call xml_stream:parse_element (ejabberd’s internal XML parser) to get the XML structure right for handling by ejabberd.

case xmlrpc_encode:payload(handle(State,Decoded)) of % handle is the xmlrpc method
 {ok, EncodedPayload} ->
    Res = IQ#iq{type = result,
        sub_el = [{xmlelement, "query", [{"xmlns", ?NS_RPC}],
        [ xml_stream:parse_element(EncodedPayload) ]
               }]
    },
%....

That’s not very optimal of course, as data is converted at nearly every step :

XML -> ejabberd -> XML -> xmerl -> XML -> ejabberd -> XML.

But developper time is more expensive than machine time, right ?

So now I can call methods on the server directly from Groovy using Groovy Jabber-RPC (using XMPPPool, of course).

If there is any interest, I’ll release the code, but for the time being it is very crude. I’ll like to “componentize” a bit more, and hook up ACLs for specific RPC handlers.

Erlang : le hasard fait bien les choses

Posted on août 23, 2007

Aujourd’hui, je voulais réaliser un prototype de serveur TCP en Erlang/OTP.

Le bouquin de Joe Armstrong contient plusieurs serveurs TCP. Un serveur de chat, un serveur SHOUTcast entre autres, et le code de la librarie lib_chan, mais rien qui utilise les principes OTP.

Et voilà qu’en arrivant sur la home de TrapExit (la ressource indispensable en erlang), je tombe sur ça :

Building a Non-blocking TCP server using OTP principles sorti le 16 août

La vie est bien faite. (Merci Serge Aleynikov ;)

UPDATE : et j’ai aussi découvert appmon:start(). Very nice.

Sam Ruby on Erlang

Posted on août 13, 2007

Sam Ruby a posté un long tutorial sur erlang.

Dans les commentaires, il ajoute : “A platform designed from the ground up to support a “distributed DataBase Management System, appropriate for telecommunications applications and other … applications with need of continuous operation and soft real-time properties” is of interest to me.”

Pareil !

Erlang : De l'infrastructure d'internet

Posted on août 10, 2007

Si vous suivez ce blog, ces derniers temps vous aurez vu quelques posts sur Erlang, le langage utilisé par Ericsson pour son matériel téléphonique et utilisé pour l’implémentation la plus performante du protocole de messagerie XMPP.

L’idée de ce post m’est tombée dessus assez rapidement, et ces derniers temps, j’ai l’impression de voir beaucoup de problématiques exposées dans d’autres langages qui sont résolue de facto par l’architecture du langage.

Deux exemples de ce mois-ci :

Adéquation avec l’orientation du hardware

Jusqu’à récemment les développeurs pouvaient se cacher derrière la loi de Moore pour garantir que leur application fonctionnerait toujours mieux. Si elle est un peu lente à sa sortie, dans 1 an, elle ira beaucoup plus vite, parce que le hardware aura (presque) doublé au niveau des performances. Mais c’est fini. La course à la puissance a lieu en multipliant le nombre de coeurs sur un processeur. Puissance qu’on ne peut récupérer que si on développe en parallélisant notre code. Ce qui pose des problèmes dans tous les domaines de l’informatique. En particulier les jeux. Comme le dit Tim Sweeney, le lead developer d’Unreal :

“Implementing a multithreaded system requires two to three times the development and testing effort of implementing a comparable non-multithreaded system, so it’s vital that developers focus on self-contained systems that offer the highest effort-to-reward ratio.”

Pour ceux qui ne lisent pas l’anglais, il faut 2 à 3 fois plus d’efforts pour mettre en place un système multithread. Les jeux ne sont pas réellement le point de ce post, mais je veux mettre le doigt sur la difficulté de maximiser l’utilisation de la puissance des processeurs actuels (dual-core ou plus).

A un niveau plus élevé, l’approche utilisée par Google et par tous les autres depuis 2000 n’est plus d’acheter un serveur incroyablement puissant (et cher), mais plutôt de prendre des serveurs peu chers (relativement peu puissants). Si j’ai besoin de plus de puissance, j’ajoute des serveurs. On peut même virtualiser complètement les serveurs en allant chez Amazon avec leur service Elastic Computing Cloud (EC2), qui permet d’obtenir une puissance de calcul virtuellement illimitée en parallélisant un grand nombre d’instance.

La grande orientation !

La solution proposée dans les deux articles précédent est toujours la même :

  • On arrête avec les threads et la mémoire partagée. Du coup si les unités de traitement ne partagent rien, on est plus obligé de synchroniser l’accès.

  • La transmission d’information se fait par passage de message, IPC-style.

C’est très exactement le schéma proposé par l’article que je citait plus haut : Nouveau patterns et middleware pour une scalabilité linéaire.

Les avantages de cette approche sont bien détaillées dans l’article de Joe Armstrong l’architecte et concepteur d’Erlang.

La scalabilité par les applications Erlang est quasiment linéaire (J’ai un CPU, la puissance totale vaut 1, j’ajoute un autre CPU la puissance totale vaut presque 2, et non pas 1,66 avec d’autres architectures qui perdent de temps en synchronisation)

L’exemple donné ici, une simulation de colonie de fourmis, le montre plutôt bien. On note que les performances d’Erlang sont largement inférieures à Haskell, mais lorsque l’application passe d’un simple core à un dual core, le temps d’exécution est divisé par deux.

Le langage est facile

Le langage est facile, oui, une fois qu’on saisi les grands concepts du langage, on comprend rapidement le code source. J’ai passé quelques temps à parcourir le code source d’ejabberd, et on rentre dedans très facilement, malgré le peu de commentaires dans le code. Evidemment, le seul mérite ne revient pas uniquement au langage, mais évidemment aux développeurs d’ejabberd, qui ont une base de code très claire et dont le style m’apprend beaucoup sur le langage.

Je remarquai la dernière fois que le code d’ejabberd est 3 fois plus compact que le code d’OpenFire. Il faut remarquer toutefois que le code d’OpenFire est bien mieux documenté :)

Mais le langage n’est pas orienté objet ?

Bien que Joe Armstrong s’en défende, il est orienté objet, en utilisant le modèle de l’Acteur, avec des objets (les processus) qui s’envoient des messages.

Certes, il n’y a pas d’héritage de classe et autres habitudes architecturales de l’orienté objet, mais c’est de l’objet. Le post de Ralph E. Johnson (oui, l’un des quatre du Gang of Four) l’explique avec beaucoup de détails.

Au passage, Johnson fait le calcul de la fiabilité des neuf 9 (99,999999% d’uptime) obtenu par le AXD 301 d’Ericsson (celui aux 1,7 millions de ligne de code d’erlang). C’est 1 seconde de downtime tous les 30 ans !

Et de toute manière, le faisait remarquer A. Zaidi sur son blog, faire de l’objet à tout prix, en particulier dans le développement d’application web (ou plus généralement de serveurs basé sur des protocoles texte, comme tous les protocoles internet), est-ce tellement pertinent ? Finalement on récupère du texte, qu’on case dans des objets, et qu’on stocke ensuite dans une base de donnée relationnel, il faut donc re-marshaller les données pour qu’elles puissent rentrer. Typiquement le genre de truc qui contribue au réchauffement planétaire.

NB : Ok, c’est un peu extrême. J’apprécie certes le modèle objet, mais lisez le post de A. Zaidi : Zen and the Art of Functional Programming.

Best Kept Secret ?

Erlang n’a pas le mindshare de Java et de Ruby et de leurs frameworks phare respectif.

Toutefois, ce langage a résolu dans son coin, là-bas, dans le nord, il y a dix ans, les problèmes de montée en charge qu’affronte les grandes applications d’aujourd’hui.

La publication de Programming Erlang chez Pragmatic Programmer semble être un succès. Et PragDave fait des tutoriaux sur son blog.

Il faut donc s’attendre à un accroissement du nombre de développements dans ce langage. Il y a déjà une première application publique développée avec le framework ErlyWeb dont je parlais ici : http://beerriot.com/ qui fonctionne aussi en application sur FaceBook.

Le blog de ce dernier est intéressant, en particulier Erlang Observations et FaceBook Development (in Erlang) sur le sujet.

ErlyWeb et Postgres

Posted on juillet 25, 2007

faut que je corrige le problème des > qui devient de &gt ;. Dans un premier temps faites le vous même.

Aujourd'hui, j'ai testé ErlyWeb, le framework web écrit en Erlang qui reprend un certain nombres d'idées à Rails.

N'ayant pas MySQL d'installé sur ma machine, mais ayant PostgreSQL, je décide de faire tourner l'application de ce tutorial de Yariv, l'auteur d'erlyweb.

Mais plutôt que de faire un site de musicien, je fais un moteur de blog. Plus simple encore que le modèle de donnée proposé par Yariv.

Et ce n'est pas trivial.

Tout d'abord, le driver PostgreSQL fourni est celui d'Erlang Consulting. Il fonctionne comme une application OTP. Donc il lui faut un fichier psql.app dans le chemin de recherche.

Un modèle est là : erlyweb-0.6/src/erlang-psql-driver/psql.app.src

ATTENTION

Avant de continuer il faut absolument que l'authentification de postgresql se fasse en MD5. C'est à dire que dans le fichier pg_hba.conf, il doit y avoir au moins la ligne suivante :


host   all         all         127.0.0.1/32          md5

Sinon le driver ne démarre pas et bloque même la console erl.

Un petit script de démarrage

Je me suis inspiré de ce tutorial.

A la racine de votre projet erlyweb, créez un fichier start.erl :


-module(start).
-export([boot/0, boot/1]).
-define(P, "/Users/cstar/devs/erlang/blog").

boot() ->
    boot(true).
boot(false) ->
    compile();
boot(true) ->
    pgsql_start(),
    compile().

pgsql_start() ->
    code:add_pathz(?P ++ "/ebin"),
    application:start(psql),
    erlydb:start(psql).

compile() ->
    erlyweb:compile(?P, [{erlydb_driver, psql}]).

On lance le bazar

Dans le terminal : yaws -i et dans la console :


1> c(start).
{ok,start}
2> start:boot().
debug:erlyweb_compile:340: Compiling Erlang file "blog_app_controller"
debug:erlyweb_compile:335: Compiling ErlTL file "html_container_view"
debug:erlyweb_compile:340: Compiling Erlang file "html_container_controller"
debug:erlyweb_compile:340: Compiling Erlang file "entries_view"
debug:erlyweb_compile:335: Compiling ErlTL file "entries_show"
debug:erlyweb_compile:340: Compiling Erlang file "entries_controller"
debug:erlyweb_compile:340: Compiling Erlang file "entries"
debug:erlyweb_compile:104: Generating ErlyDB code for models: "entries.erl "
{ok,{{2007,7,25},{19,1,20}}}

Tout va bien ; C'est parti ! On pointe sur http://localhost:4000/blog/entries/ (à ajuster selon la config de Yaws)

Ca marche pô !

Postgresql se plaint de la syntaxe de LIMIT X,Y. Lui il veut LIMIT Y OFFSET X. Ouvrez un autre terminal. Rapidement le problème est identifié dans la ligne 309 du fichier erlyweb-0.6/src/erlsql/erlsql.erl. Il faut la remplacer par la ligne suivante :


    [<<" LIMIT ">>, encode(Num),<<" OFFSET ">> , encode(Offset)];

Ensuite à la racine de ErlyWeb, on refait un make. Il faut maintenant recharger le code de erlsql.


3>l(erlsql).

Il ne reste qu'à recharger la page dans le navigateur, et de profiter de notre application.

Notes de fin

  1. Je voulais consacrer plus temps à l'utilisation du framework. C'est un peu gaché.

  2. Modifier directement le code du framework c'est pas terrible non plus. Mais j'ai pas encore trouvé de solution propre, vu que le driver erlydb ne peut pas indiquer ses préférences à erlsql. Je posterai sur la ML pour en parler.

  3. Au final, je voulais surtout utiliser Mnesia, j'aimerai pouvoir interconnecter mon appli erlyweb avec ejabberd. Ca fera l'occasion d'autres post.

Programming Erlang : La correction de l'exercice !

Posted on juillet 23, 2007

Sur la page 158 de Programming Erlang, il y a un exercice. Faire un anneau de N processus. On envoie le message à l’un deux, et le message parcourt l’anneau M fois.


-module (exoc).
-export([start/2]).

start(Max,Tours) ->
    start(Max,Max,0, Tours).

start(0, _Max,PrevPid, _Tours)->
    PrevPid;
start(Max, Max, _Pid, Tours) ->
    F = fun() ->
            LastPid = start(Max-1, Max, self(), Tours),
            loop(LastPid, Tours)
        end,
    spawn(F);
start(N, Max,PrevPid,Tours) ->
    Pid=spawn(fun() -> loop(PrevPid, Tours) end ),
    start(N-1, Max, Pid, Tours).

loop(_ToPid,0) ->
    io:format("~nFini pour :~p",[self()]);
loop(ToPid, Tours) ->
  receive
    M->
     ToPid ! M,
     io:format("~n(~p) :~p -> ~p.",[self(),M, ToPid]),
      loop(ToPid, Tours-1)
  end.

Vers Erlang via XMPP

Posted on juillet 21, 2007

XMPP , né Jabber, m’occupe depuis maintenant quelques mois.
Ce protocole s’occupe de la messagerie instantanée, et se cache derrière le réseau GoogleTalk. Une de ses grosses forces est l’ouverture et l’interopérabilité entre les serveurs XMPP. Il faut imaginer le réseau XMPP comme le réseau de routage des emails. L’utilisateur est indentifié via son jid (Jabber ID) : eric@cestari.info . Ca ressemble à une adresse mail, et pour cause. Un ami, Olivier, est sur GoogleTalk. Son jid est of@gmail.com . Lorsque je souhaite envoyer un message, il est routé par mon serveur (cestari.info) vers le serveur gmail.com qui délivre enfin le message à Olivier.

Intégré aussi, le système de chatroom, de publication/abonnement, annuaire, etc.

Bref, le protocole de messagerie instantanée qui finira par gagner :). Je voulais installer un serveur XMPP sur mon serveur, pour jouer avec et écrire une version custom du module MUC (multi-user chat, les chatrooms). Parmi les serveurs XMPP les plus répandus, j’en ai testé 2 et gardé 1. OpenFire : Ecrit en Java, administré via http. Respecte les principales XEP (extensions de XMPP). Dimensionné pour 5000 utilisateurs simultanés, avec la possibilité d’utiliser des connecteurs pour augmenter la capacitée.
J’ai eu un problème de connexion S2S (Server to Server) vers d’autres serveurs comme GoogleTalk. J’ai arrêter de creuser dessus, bien que Java soit le langage que je connaisse le mieux. ejabberd : Ecrit en Erlang (on y arrive !), très bonne couverture du protocole et de ses extensions. Et ensuite, les trucs mortels :

  • Mise en cluster : J’ai besoin de plus de puissance ? j’ajoute un serveur avec une nouvelle instance d’ejabberd que je connecte en 30 secondes, sur la première.

  • Possibilité de mettre à jour le code sans interruption : Je compile, je me connecte à la console du serveur, je lui dit que le code d’un module a été mis à jour, et les requêtes suivantes seront traités par la nouvelle version.

  • Scalabilité incroyable : on peut lui balancer des utilisateurs … Il existe des installations d’ejabberd qui gèrent des centaines de milliers d’utilisateurs et des dizaines de milliers d’utilisateurs simultanéments. Un simple PIII 1,5GHz gère 2000 messages par seconde et 7000 utilisateurs connectés !

Toutes ses fonctionnalités arrivent avec un coût. Tout est écrit en erlang . Langage inventé par les suédois d’Ericsson, pour avoir un outil adapté au développement de leurs produits télécom. Ce petit film indépendant très cheesy présente les avantages d’Erlang. Avant de cliquer sur ce lien, n’oubliez pas qu’il s’agit des concepteurs du langage eux-même qui s’occupent de la présentation.

Je regarde un peu le code source d’ejabberd, et je comprends initialement que dalle. Regardez plutôt.

Interlude Java ….

Pendant la phase de réflexion, je m’oriente vers des bots XMPP qui répondrait aux requêtes. J’utilise la librairie Smack , par Jive Software. (oui ceux derrière OpenFire !). C’est du Java, je connais. En quelques heures et 300 lignes de code j’ai deux bots qui se connectent à mon serveur, qui causent entre eux et gèrent des chatrooms.

Mais ca ne me plaît pas. Même si l’informatique distribué, c’est toujours marrant, le côté mystérieux et “best kept secret” m’intrigue.

En effet, les avantages d’ejabberd sont directement des avantages du langage et de l’environnement Erlang. Il fonctionne sur une machine virtuelle comme Java.

Du coup, je creuse. Je tombe sur des blogs : Tenerife Skunkworks , Joel Armstrong (l’un des concepteurs, justement) et Yariv’s Blog , qui parlent causent erlang.

Des PDF intéressants : Getting started with Erlang La thèse de Joel Armstrong

Et comme par hasard, ce dernier vient de publier chez Pragmatic Programmers , Programming Erlang .

J’achète le PDF (des bouts sont dispos sur la page du livre). Pas le temps d’attendre la livraison, et avec un dollar aussi bas … c’est cadeau.

Et finalement, petit à petit, le code source de ejabberd commence à prendre sens.

Après 2 jours de travail, et une dizaine de programmes de test, et une première bidouille sur un module d’ejabberd, je sens que je peux vous parler de manière plus informée de ce langage.

Les caractérisques notable d’Erlang :

  • Langage fonctionnel, comme Lisp et OCaml
  • Donc largement basé sur les closures et fonctions anonymes, ce qui rend la programmation événementielle très pratique à utiliser.
  • La communication se fait uniquement par passage de message d’un processus à l’autre, il n’y a pas d’état global
  • Les processus sont complètement isolés, donc pas de synchronisation à faire. Chaque processus a une boite à message qu’il relève avec receive. Les processus sur des noeuds (instances) séparés peuvent communiquer très simplement.
  • La création d’un processus, et le changement de contexte ne coûtent rien. Un petit script qui va créer 20000 processus créera chacun d’eux en 2,5 ms.
  • Le pattern matching et les variantes de fonctions permettent d’exprimer la gestion des cas particuliers :

 -module(testblog). 
 -export([hello/1]). 

 hello({hello, eric}) ->
     io:format("Bonjour Eric~n"), 
     ok; 
 hello({hello, Name}) ->
     io:format("Bonjour, ~w~n", [Name]), 
     ok; 

 hello({Action, Name}) ->
     io:format("action : ~p pour : ~p~n", [Action, Name]), 
     ok. 

Le tuple (le truc entre {}) passé en paramètre sera testé sur chacun des prototypes en partant du haut. Les mots commençant par une minuscule sont des atomes (utilisés entre autres comme clés. hello represente un truc qui s’appelle hello et qui peut être identifié par hello.) Les mots commençant par une majuscule sont des variables.

Donc :

  • testblog:hello({hello, eric}) appelera la première version.
  • testblog:hello({hello, marc}) ne sera pas matché par la première version, car marc est différent de eric. On teste sur la deuxième. Le premier élément est l’atome hello, et le deuxième est une variable, que je peux matcher avec n’importe quoi (donc marc). Il n’y a pas d’autres éléments dans le tuple, donc cette version de la fonction convient. C’est donc celle-ci qui est exécuté.
  • testblog:hello({bonjour, eric}). Seule la troisième version de la fonction accepte un premier paramètre différent de 0.

Enfin bref, ce billet n’est pas un tutorial sur Erlang, c’est un truc qui peut vous donner envie de l’utiliser.

Une fois qu’on sait lire la syntaxe du langage (ce qui ne prend que quelques heures), on commence à apprécier la clarté compacte qu’elle engendre.

Du coup, je me suis livré à une étude non-scientifique sur la complexité relative d’un gros projet entre Java et Erlang, en regardant le nombre de lignes entre ejabberd et OpenFire. Alors que OpenFire compte à peu près 200 000 lignes , ejabberd n’en compte que 80 000 , avec plus de fonctionnalités.

Damn !

Un autre exemple, comparaison des performances entre Apache et yaws (serveur web écrit en erlang). Le benchmark s’attache au nombre de connexions simultanée.
Apache lâche autour de 4000 connexions simultanées. Yaws fonctionne toujours à 80 000 connexions simultanées.

Duh !

Pour démarrer, Process One, le sponsor de ejabberd maintient CEAN (Comprehensive Erlang Archive Network), le CPAN d’Erlang. Avec des packages pour démarrer .

J’en re-parlerais très bientôt. Pour l’heure, il est samedi, et Martin veut jouer avec son papa.