Trucs de geek

Des bots XMPP avec Smack

Posted on juillet 26, 2007

J’ai écrit il y a quelques temps deux bots qui communiquent entre eux. j’utilise la librarie Smack Java.

Le code : chatbots.zip. C’est un projet Eclipse 3.1, avec toutes les jars nécessaires.

Pour l’utiliser, vous allez devoir créer deux comptes sur le serveur Jabber, et modifier la méthode main.

Mais que font-ils ?

Il y a deux bots : sessioncreator et sessionbot. Pour créer une chatroom, il faut envoyer un message à sessioncreator sous cette forme :

create MaChatRoom <liste de personnes a inviter separes par des espaces>

A ce moment-là, sessioncreator :

  • crée la chatroom et met le sessionbot en owner de la chatroom.
  • invite le sessionbot, avec le nom listener
  • envoie les invitations au demandeur, et à la liste des personnes à inviter.
  • quitte la chatroom un fois que le sessionbot a rejoint la chatroom.

Le sessionbot, lui travaille beaucoup moins :

  • Il accepte les invitations à rejoindre les chatrooms
  • Il kick toutes les personnes qui disent FORBIDDEN.

NB : le sessioncreator a deux hooks canCreate et canJoin qui permettent de vérifier si un utilisateur peut créer ou joindre la chatroom (avec une vérification en BDD, par exemple).

NB2 : Vous pouvez utiliser/modifier mon code source sans restriction. Pour les jars de Smack, vous devez respecter leur licence (mais bon c’est du Open Source Apache, ce qui est assez tranquille).

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.

First Post

Posted on juillet 23, 2007

Le nouveau blog est en ligne ! Je garde l’autre toujours en ligne pour l’instant, avec toutes les archives.

Mais comme s’abonner à un site avec pas de contenu, c’est un peu pourri, j’ai réimporté quelques posts plus ou moins récents.

Bonne lecture !

(Mephisto, voilà un outil de geek … Mais je savais bien qu’il fallait qu’iWeb me claque dans les doigts pour me bouger ! )

[UPDATE : iWeb s’est réparé tout seul … Il est possible que ce soit un problème de backend chez Apple, finalement. Mais c’est salutaire !

Je vais pouvoir tweaker mon moteur de blog à l’envi.]

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.

iWeb = iBoue

Posted on juillet 23, 2007

J’ai perdu les commentaires … Je me prends des messages d’erreur à la publication. Faut que je fasse pointer mon blog vers quelque chose d’autre, mais je n’ai pas encore décidé quoi ? (Typo, Mephisto, WordPress, Blogger ?)

J’aime la possibilité de d’écrire mes posts sur un client plutôt lourd (pas de boite de dialogue, facilité d’insertion des images etc)

Une combo MarsEdit / qq chose qui tourne sur ma dédibox probablement …

EDIT : ce sera Mephisto

Libraries et clients XMPP

Posted on juillet 21, 2007

Le post précédent était trop long. J’espère que vous avez tenu.

Indispensable pour développer des applications XMPP, le client Psi . Permet d’afficher le trafic depuis et vers le client, la possibilité de composer en brut les stanzas (paquets) XMPP, connexions multiples, bref idéal pour développer et débugger.

  • Pour causer :

    Il y aussi iChat sur les Mac, GAIm et le WengoPhone .
    Un client plutôt sympa : Coccinella . Avec une fonctionnalité de whiteboard en SWF. Malheureusement l’interface utilisateur est plutôt maladroite.
    La page des clients sur Jabber.org

  • Pour développer :

    Les APIs que j’ai testée sont en général basé sur un modèle événementiel, ce qui colle plutôt bien :) Il y a Smack dont je parle dans le post précédent qui gère tout le support de XMPP-Core, et entre autres, la connexion au serveur de discussion, et les data-forms (envoi de formulaires à la HTML).

    C’est une librairie Java, et elle semble la plus avancée dans ce langage. Par contre, pas de développement de composants, il faut utiliser Whack , qui n’est curieusement pas documenté du tout sur le site IgniteRealtime.

    J’ai testé XMPP4R, mais pas longtemps. J’ai joué avec les exemples, remarqué que la couverture de XMPP Core et des Extensions est large, et y a des helpers pour développer des Multi-User Chatrooms (MUC) en composant assez facilement. Par contre j’ai eu des problèmes curieux pendant le développement de mes bots, et par flemme (?) j’ai basculé mon développement sur Smack en Java. Les librairies XMPP sur Jabber.org

. . . Enough pour aujourd’hui.

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.

JRuby 1.0 : experiences

Posted on juillet 02, 2007

J’ai développé une application Rails pour Ohm Force en interne. Il est un peu trop tôt pour parler du contenu de cette application qui aura de toute manière une portée limitée – ce sera un extranet, qui devrait être en soi une expérience intéressante.

JRuby : Késako ?

Ruby est un langage de script plutôt populaire, et largement popularisé par Ruby On Rails, le framework qui rend le développement web amical. Il existe plusieurs interpréteurs Ruby. Le plus connu est l’implémentation de référence, celle qu’on appelle la MRI (Matz’s Ruby Interpreter ou Matz’s Reference Implementation) qui est développée principalement par Matz, l’inventeur de Ruby.

Depuis quelques temps, la machine virtuelle Java s’est enrichie d’autres langages que le Java. Le plus connu est probablement Groovy . Mais certains se sont mis en tête de développer une implémentation de Ruby sur la JVM. Ca a bien plu à Sun qui a embauché les devs principaux de JRuby.

JRuby : Pourquoi ?

  • Parce qu’on peut le faire !

  • Avec la popularité de Java en entreprise, cela permet très facilement de réutiliser des classes et des objets Java directement en Ruby.

  • Les conteneurs d’application Java ont 7 ans d’existence, sont fiables et permettent une grosse montée en charge, ce que les techniques d’hébergement actuelles Rails ne permettent pas avec autant de souplesse.

JRuby : A Ohm Force ?

C’est pas un mystère pour ceux qui lisent ce blog, mais ca fait un moment que je suis à fond dans Ruby, et j’aimerai bien que mes développements futurs soient en Ruby. Actuellement à Ohm Force, on utilise Tomcat 5.5 pour le site web, avec un suite de Frameworks relativement standards : Struts, OJB, ACEGI, une pincée de Spring, Javamail. Il est évident que si on doit déployer une nouvelle application, si c’est une webapp avec des servlets et des JSP, on sait faire en interne.

L’appli est maintenant déployé sur le serveur de test en attendant une validation (qui finira bien par arriver ;) )

Et c’était facile ?

Oui et non.

Oui : pour le développement, c’est plutôt simple, une fois que JRuby est installé, il suffit de taper jruby plus ruby dans la console, et ca marche. Le démarrage est un peu plus long qu’avec la MRI, parce qu’il faut que la JVM démarre, mais une fois en route, les performances semble vraiment similaires.

Pour le déploiement par contre, j’ai eu plus de problèmes qui m’ont coûté 2 jours et quelques surprises.

La mémoire !

La JVM limite la mémoire disponible. Par défaut, le tas est à 64Mo, c’est très largement insuffisant pour l’intégration Rails sur Tomcat. Avec 256Mo, ca passe beaucoup mieux :) Donc JRuby coûte très cher avec Tomcat. J’avais 4 webapps qui tournaient sur 64Mo de RAM sur mon MacBook Pro, donc évidemment peu de trafic. En ajoutant mon appli sous JRuby, les besoins passent à 170Mo. Pour une utilisation mono-utilisateur …. Par contre, mon analyse me laisse à penser que plus de trafic et une application Rails beaucoup plus complexe n’augmentent pas significativement l’occupation mémoire.

Toutefois il faut être prudent dans le développement/portage d’applis sous JRuby. La MRI demande au système autant de mémoire que nécessaire, attaquant la swap si nécessaire. JRuby est complètement limité par la mémoire allouée à la JVM, et si ça dépasse, on se prend ça :

  java.lang.OutOfMemoryError : Java heap space.

Et ca fait tomber aussi les autres applis dans le même conteneur, vu qu’elles n’ont plus de mémoire non plus.

Le réglage de la JVM : Il faut passer les paramètres suivant à la JVM pour un tas de taille fixe de 256Mo : java -Xms256m -Xmx256m

L’intégration et Tomcat : GoldSpike

Il y a un plugin Rails qui s’appelle GoldSpike .
Ce plugin contient :

  • Des tâches Rake pour construire un WAR

  • 2 servlets qui servent les fichiers dans public/ et l’application Rails proprement dite.

L’utilisation est assez simple, mais plusieurs problèmes se sont présentés. GoldSpike embarque la jar deJRuby, sauf qu’il utilise la version 0.9.9. J’ai du aller modifier dans le code source la version de la Jar : Dans le fichier lib/war_config.rb à la ligne il faut mettre


addjavalibrary(maven_library('org.jruby', 'jruby-complete', '1.0'))
Ensuite pour ajouter les jar autres nécessaires à mon projet (le driver Postgres et une jar de classes internes à Ohm Force), il a fallu lire un peu de code source pour comprendre. La recette :

  1. Copier les jar dans lib/java de votre projet Rails

  2. Créer un fichier config/war.rb avec pour chaque jar la ligne :

    
    include_library 'postgresql', :version=>'8.0', :locations => 'lib/java'
  3. rake war:standalone

L’archive est bien construite il faut l’envoyer sur le tomcat.

Finalement j’ai décompressé le war, et c’est ce que j’ai créé comme projet sur le subversion de boite. Plus simple de déployer, et ça me fait une arborescence qui marche à la fois avec Mongrel et sous Tomcat.

Une fois détectée par Tomcat, l’archive s’installe et jruby est lancé.

Mais c’est pas fini, il faut que je modifie le schéma de la base. Problème, je n’ai pas rake. Du coup je suis obligé d’installer JRuby sur le serveur avec un set de gem minimum pour que rake db:migrate fonctionne. Faudrait creuser quand même, je pense qu’on peut faire mieux :)

L’appli démarre !

C’est pas encore fini, sur mon MacBook Pro, il faut une dizaine de secondes pour que les interpréteurs ruby soient lancés et que Rails commence à accepter les requêtes.

Comment ça marche ? Ce paragraphe fait suite à une analyse très superficielle du code source des servlets GoldSpike .
Rails n’est pas du tout threadsafe. Vraiment pas. Donc ce que fait la servlet, c’est charger un certain nombre d’interpréteurs Ruby qui chargeront chacun une instance de l’application Rails. (D’où les problèmes de mémoire). Du coup chaque instance Rails est isolée.

Affiner la configuration de la webapp. Dans le fichier WEB-INF/web.xml, on peut passer des paramètres à la servlet pour indiquer le nombre d’instance de Rails il démarre :


 <servlet> 
  <servlet-name>rails</servlet-name> 
  <servlet-class>org.jruby.webapp.RailsServlet</servlet-class> 
    <init-param> 
        <param-name>jruby.pool.maxActive</param-name> 
        <param-value>4</param-value> 
    </init-param> 
        <init-param> 
        <param-name>jruby.pool.maxIdle</param-name> 
        <param-value>4</param-value> 
    </init-param> 
        <init-param> 
        <param-name>jruby.pool.minIdle</param-name> 
        <param-value>2</param-value> 
    </init-param> 
      <init-param> 
        <param-name>jruby.pool.timeBetweenEvictionRunsMillis 
            </param-name> 
        <param-value>10000</param-value> 
    </init-param> 
 </servlet> 
   
J’ai mis les valeurs par défaut. Le dernier paramètre correspond au temps entre deux invocations du thread destructeur d’instances.

ActiveRecord-JDBC

Pour se connecter à la base, JDBC est là ! Et la gem activerecord-jdbc permet de se connecter à une base de données directement ou via un pool de connexions managé par Tomcat.

Pour conclure

Jusque-là mes expériences ont été globalement positives. J’ai eu des petits soucis, mais comme à chaque fois lorsqu’on teste une nouvelle techno. J’attends évidemment les versions à venir de JRuby qui vont maintenant se focaliser sur la performance – JRuby est un interpréteur valide de Ruby 1.8.5. Pour le passage en production, je m’inquièterai juste de la consommation mémoire.

Pas de fuites toutefois. La JVM reste stable autour de 170Mo depuis 4 jours et 17h.

[MAJ : Je ne poste plus trop sur le sujet – ça marche bien et pas de problème.]