Trucs de geek

Clustering OpenFire with Shoal (Sort of ...)

Posted on avril 09, 2008

As requested there, the code I wrote for clustering OpenFire with Shoal.

It’s not functional. The cluster lifecycle is ok. Getting Synchronous and Asynchronous tasks running on the cluster work within MUC.

There’s still a lot of work.

I don’t plan working on this code any further, but if it can inspire someone in carrying it on …

Here it is.

Auth me on Jabber

Posted on mars 07, 2008

CAS est un service très populaire permettant de mettre en place le Single Sign On, avec des librairies clientes pour la plupart des langages.

On peut facilement rajouter des handlers d’authentification.

En voici un très rapide, permettant de s’authentifier sur un service Jabber.

Installation :

Uniquement compatible CAS v3.

Installez ce bout de xml dans le deployerConfigContext, dans la liste des AuthHandlers :


<bean class="com.ohmforce.cas.JabberAuthenticationHandler">
 <property name="jabberHostname" value="DOMAIN"/>
</bean>

Copiez jabber-cas.jar.zip et la jar de Smack, relancez la webapp CAS et c’est tout.

Pour se logger sur vos applications CASifiées, vos utilisateurs utiliserons leur compte défini dans sur le domaine indiqué par jabberHostname, le mal nommé.

Le code

Y en a tellement peu que ca tient ici.


package com.ohmforce.cas;
import org.jasig.cas.authentication.handler.AuthenticationException;
import org.jasig.cas.authentication.handler.support.AbstractUsernamePasswordAuthenticationHandler;
import org.jasig.cas.authentication.principal.UsernamePasswordCredentials;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;

final public class JabberAuthenticationHandler extends
        AbstractUsernamePasswordAuthenticationHandler {
    private String jabberHostname;
    @Override
    protected boolean authenticateUsernamePasswordInternal(
            UsernamePasswordCredentials credentials)
            throws AuthenticationException {
        XMPPConnection conn = new XMPPConnection(jabberHostname);
        try {
            conn.connect();
            conn.login(credentials.getUsername(), credentials.getPassword());
        } catch (XMPPException e) {
            log.error("Failed", e);
            return false;
        }
        finally{
            conn.disconnect();
        }
        return true;
    }

public void setJabberHostname(String server)
{
    jabberHostname=server;
}
public String getJabberHostname()
{
    return jabberHostname;
}

}

GlassFish

Posted on février 15, 2008

Ça y est, le serveur d’application Ohm Force est passé sous GlassFish 2.

Maintenant le serveur est organisé avec :

  • nginx en frontal, configuration simple et courte,
  • PHP en FCGI pour le site vitrine de GForceSoftware,
  • GlassFish pour l’hébergement d’Ohm Force, du site magasin de GForce (c’est le même code) (des applis Struts/OJB/Spring) et du site de VDM, une appli jRuby on Rails.
  • le tout connecté à un PostgreSQL 8.

Avant le setup était globalement le même, mais avec Tomcat 6.

GlassFish

Plutôt agréable à utiliser. C’est une grosse machinerie qui se laisse manipuler assez facilement. Toutefois la doc est vraiment volumineuse, et rien que chercher les infos dedans va prendre du temps.

Au cours du “portage” depuis Tomcat, j’ai utilisé 2-3 trucs :

touch .reload

Ca force GlassFish à recharger la webapp, si c’est fait à la racine du répertoire de celle-ci. Il faut avoir configurer GlassFish pour autoriser l’autoriser.

C’est plus agréable au final que la méthode automatique de Tomcat. On a le droit de choisir quand ça arrive :)

Par contre ce redéploiement change la conf des webapp dans le domain.xml

asadmin set server.http-service.virtual-server.server.property.allowLinking=true

Permet d’utiliser des liens symboliques dans les webapps servies par le serveur virtuel server.

~/.asadminpass et ~/.asadmintruststore

Ces fichiers stockent les identifiants pour administrer GlassFish via asadmin.

asadmin deploydir <i>rep</i>

Déploie ou redéploie la webapp dans le répertoire rep.

$GLASSFISH_ROOT/domains/domain1/config/domain.xml

Le fichier de configuration, celui modifié par l’interface d’admin. Evidemment, il faut redémarrer le serveur après une modification.

$GLASSFISH_ROOT/domains/domain1/lib/

Les jars placées ici sont accessibles dans tous les classloaders du domaine. Pour pouvoir utiliser les datasources avec PostgreSQL, on place la jar du driver dans le sous-repertoire ext/ (et pas databases/!)

Attention …

GlassFish a tendance à cacher les fichiers des webapps $GLASSFISH_ROOT/domains/domain1/generated.

Faire un rsync entre deux serveurs pour synchroniser le répertoire de glassfish, c’est une mauvaise, très mauvaise idée. Les webapps ont récupéré la configuration de l’autre serveur, et les droits.

Les trucs biens

GlassFish semble vraiment plus solide que Tomcat. Il fait tourner les 3 webapp sans problème, alors que Tomcat ne supportait pas d’avoir l’appli jRuby on Rails, et crashait avec un vilain problème de mémoire.

L’interface d’admin … vraiment top. Quel changement ! J’utilisais Probe sur Tomcat, mais c’est pas aussi bien :)

Les trucs louches

Au démarrage, la présence de l’appli jRuby on Rails fait pomper au serveur 99% des ressources pendant plusieurs minutes. Je n’ai pas encore réussi à comprendre pourquoi … surtout qu’après on redescend à une charge tout à fait raisonnable. Mais j’aimerai comprendre …

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 :)

NetBeans, mon nouvel IDE préféré ?

Posted on octobre 18, 2007

Je suis en train de tester NetBeans 6, en particulier pour mon développement Ruby on Rails.

Parmi les fonctionnalités les plus intéressantes (lire : celles qui me manquaient sur TextMate), on trouve

  • l’intégration du debugger Ruby, qui fonctionne comme ça doit (un breakpoint dans la marge, l’exploration de la pile, les watchs et tout)

  • la complétion ; plutôt intelligente pour une complétion de langage dynamique (donc plus dure à implémenter que pour Java)

  • le support de Ruby on Rails : support du script generate (bon ok, c’était déjà dans TextMate, mais si je change, je ne vais pas revenir en arrière), templates RJS, RHTML etc.

  • support Subversion natif. Comme ci-dessus, c’était déjà dans TextMate et dans Eclipse, mais je vais pas cracher dessus non plus …

J’avais testé une béta précédente qui ne m’avais pas autant impressionnée, mais là, respect, Sun.

Et pour finir, embarqué avec NetBeans, JRuby 1.0.1.

Maintenant, il va falloir voir à l’usage, et sur la longueur, le test des 3 minutes est réussi :)

[UPDATED] XMPPPool : XMPP Connection Pooling for Tomcat and Grails/Jetty

Posted on septembre 10, 2007

Release 0.3 of XMPPPool

[Update : also added support for Grails Jetty]

[Update 2 : Simplified the API a lot. It is now basically invisible …]

About

It provides XMPP connection pooling for Tomcat, configured as a resource in the $TOMCAT_HOME/conf/server.xml.

The main use case to use Groovy Jabber-RPC, with the webapp acting as a client :

  • You fetch a XMPPConnection from the pool,
  • call an RPC method,
  • get the result,
  • return the XMPPConnection to the pool.

Of course you can use the code to write XMPP “servers”/bots, but you’ll have to manage the lifecycle of the connection (i.e. launch a separate thread)

How it works

A pool is created, with connections to the XMPP server. All connections use the same XMPP account, but each have a unique resource.

Rule 10.5 of RFC 3920 (XMPP Core) means that is a stanza is sent to a JID with a specific resource, it MUST be delivered to this resource or it MUST fail. However if a stanza is sent to a jid without any resource identifier, it SHOULD be sent to at least one of the resources.

So make sure that the code answering to a query coming from an XMPPPool managed connection sends it to a specific JID.

Quick install guide for Tomcat

Download XMPPPool-0.3.0-jar-with-dependencies.jar.zip and copy it in your $TOMCAT_HOME/common/lib. Then in server.xml, configure your source as follow :


<Resource auth="Container" maxActive="10" maxIdle="5" maxWait="10000" 
  factory="com.ohmforce.xmpp.XMPPSourceFactory" type="com.ohmforce.xmpp.XMPPConnectionPool" name="xmpp/connection"
  username="username" password="pass" service="localhost" port="5222"/>

The pooling code is using commons-pool from Apache. All the usual options are available. port is optional and defaults to 5222.

Now restart your container.

Quick install guide for Grails/Jetty

First you need to patch Grails as per this report (which should be implemented soon in Grails). Don’t forget to add the jars to $GRAILS_HOME/lib!

Also unzip and add XMPPPool-0.3.0-jar-with-dependencies.jar in GRAILS_HOME/lib

Then add a jetty-env.xml file in WEB-INF/ directory (of your Grails app)


<Configure class="org.mortbay.jetty.webapp.WebAppContext">
  <New id="xmpp" class="org.mortbay.jetty.plus.naming.Resource">
    <Arg>xmpp/connection</Arg>
    <Arg>
    <New class="com.ohmforce.xmpp.XMPPSourceFactory">
     <Set name="properties">
       <New class="java.util.Properties">
         <Put name="username">username</Put>
         <Put name="password">pass</Put>
         <Put name="service">localhost</Put>
       </New>
      </Set>
   </New>
 </Arg>
   </New>
</Configure>

Start the grails app : grails -Denable.jndi=true run-app

Now inside a grails controller you can connect to a Jabber-RPC service :

Using from Grails :

Now inside a grails controller you can connect to a Jabber-RPC service :


import javax.naming.InitialContext;
import groovy.net.xmlrpc.*
class HelloController{
def hello = {
        def p =new InitialContext().lookup("java:comp/env/xmpp/connection")
        def serverProxy = new JabberRPCServerProxy(p, "cstar@localhost")
        def s = serverProxy.add(2,6)
        def e = serverProxy.echo("toto")
        flash.message = "${s} and ${e}"
        p.disconnect() // does not actually disconnect, but returns connection in pool
    }
}

The server code (slightly modified from the examples on the Groovy website. fixed typos and updated to latest Smack version) :


import groovy.net.xmlrpc.*
import org.jivesoftware.smack.XMPPConnection
def server = new JabberRPCServer()
server.echo = {return it+"toto"} 
server.add = {i,j -> return i+j} 
def serverConnection = new XMPPConnection("localhost")
serverConnection.connect() 
serverConnection.login("user", "pass") 
server.startServer(serverConnection)

Using the datasource in a servlet


try{
    InitialContext ctx = new InitialContext();
    XMPPConnection conn = (XMPPConnection) ctx.lookup("java:/comp/env/xmpp/connection");
    // Do things here
    conn.disconnect(); // does not actually disconnect, but returns connection in pool
}
catch(Exception e){
    e.printStackTrace();
}

For actually using the connection, please refer to the Smack developper guide

Building the source code

The project is built with Maven.

mvn assembly:assembly

cp target/XMPPPool-0.3.0-jar-with-dependencies.jar $TOMCAT_HOME/common/lib/

And you’re set.

Bugs ? Patches ?

  • mail : eric at ohmforce dot com
  • xmpp : cstar at cestari dot info

Known issues

  • Only one pool can be configured on a given tomcat instance (Although that should fit most use cases)

Todo

  • Better error reporting, I guess :)

License

Apache 2.0 license, the same as Smack and others libs XMPPPool is built upon. Free to use, as long as you don’t sue me or remove my name from the code …

Download

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).

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.]