Active MQ dans le cloud

En ce moment je regarde pour développer une plateforme de push notification iPhone. Le but est d’offrir une surcouche applicative plus simple à utiliser que les sockets d’Apple. Un peu comme le service offert par urbanairship.

Ce qu’il faut bien comprendre avec le push d’Apple, c’est que pour envoyer une notification à 400 000 iPhone il faut ouvrir une socket chez eux et envoyer 400 000 trames avec un token différent par iPhone. En sachant qu’Apple peut banir une IP serveur si on envoie plus de 200 messages par seconde, il est important de répartir la charge sur plusieurs serveurs (ou au moins plusieurs IP publiques).

Donc j’ai pensé à mettre en place une architecture MOM :

photo

Avec cette architecture on peut ajouter des Brokers JMS en fonction de la charge à absorber. L’idée est de profiter au maximum des services de cloud computing et offrant la possibilité d’ajouter/retirer au runtime des VM avec un Broker. C’est donc pour héberger cette solution que j’ai creusé les différentes offres de cloud computing (cf ce post). Mais c’est au moment du déploiement que j’ai rencontré des difficultés (bah oui comme d’habitude en fait…).

Le code qui va envoyer les messages JMS doit connaitre la liste de Broker. Or comme nous sommes dans le cloud, cette liste peut être modifiée au bon vouloir de l’administrateur. Pire je ne connais même pas forcément à l’avance les IP des Broker que je vais ajouter.

Donc imaginez si dans la configuration client je définis une URI du type :

failover:(tcp://primary:61616,tcp://secondary:61616)

Il va falloir modifier le code du client à chaque ajout d’un nouveau Broker. C’est donc impossible. Heureusement pour ce genre de cas Active MQ propose un système de multicast. Ce qui donne au niveau URI du client :

discovery:(multicast://default)

Là c’est magique sur mon serveur en local ça marche très bien. Et oui mais non :(Sur les offres cloud le multicast (sur UDP) n’est pas souvent supporté. Sur Amazon EC2 (donc sur CloudBees) et CloudFoundry (VMWare) impossible de faire fonctionner le multicast.

Donc comment faire ? Le client JMS a besoin de récupérer une liste de Broker. Il faut donc que chaque Broker vienne se faire connaitre au moment de leur lancement. Comme j’ai déjà un BDD autant l’utiliser pour référencer ces Brokers. J’ai donc développer un module de multicast via une BDD : multicasdb.

Donc on arrive sur une architecture comme celle-ci :

photo

Pour le faire je me suis beaucoup inspiré du code du multicast UDP. Ca fonctionne bien sur mes serveurs de test mais là encore je tombe sur des problèmes spécifiques aux serveurs cloud : l’adresse du serveur. Comme c’est le même war qui est déployé sur tous les serveurs la configuration du broker doit être générique. Pas de pb : j’utilise localhost comme nom de serveur :

<broker persistent="false">
  <networkConnectors>
    <networkConnector uri="multicastdb://default?dataSource=myDataSource">
</networkConnector>
  <transportConnectors>
     <transportConnector uri="http://localhost:8080"   
discoveryUri="multicastdb://default?dataSource=myDataSource">
    </transportConnector>
  </transportConnectors>
</broker>

Donc oui je peux utiliser cette configuration sur tous les serveurs, mais si je persiste “localhost:8080” en base le client ne pourra pas y accéder depuis un autre serveur. Donc il faut que mon code trouve l’IP du serveur pour le persister au runtime.

Aujourd’hui même si le code n’est pas finalisé, il fonctionne. Malheureusement j’ai toujours des problèmes pour l’héberger.

Chez Cloudbees la communication tcp entre VM est bloquée. Même si leur support affirme que ça peut fonctionner j’ai toujours des erreurs : java.net.ConnectException: Connection refused

Chez CloudFoundry j’ai même eu du mal à obtenir l’IP des VM. Le code qui fonctionne sur Cloudbees retourne 127.0.0.1 comme IP chez CloudFoundry. Pour trouver cette IP il faut utiliser un SDK fourni. Mais là encore :Connection refused :(

Bilan

Au final je n’ai pas réussi à faire fonctionner mon code comme je le voulais. Par contre cette petite expérience m’a permis de faire mes premiers pas sur 2 offres de cloud intéressantes. Aujourd’hui je trouve CloudFoundry beaucoup plus agréable à utiliser que CloudBees. Son SDK est très simple et en 20 secondes je peux redéployer une version. Avec CloudBees j’ai beaucoup de mal avec le SDK. Déjà c’est basé sur un script Ant… Alors il y a aussi un plug-in maven mais je n’ai pas pu l’utiliser en mode “Delta” du coup il faut uploader totalement mon War à chaque modification : 10 minutes. Je trouve aussi le plug-in Eclipse de CloudFoundry mieux fait. Par contre CloudBees possède une vrai interface d’administration web et son offre DEV@Cloud est vraiment intéressante. Un bon point pour les 2 : la réactivité du support. Une réponse dans la journée, pour mon compte gratuit de test, c’est vraiment bien (n’est ce pas Nicolas 😉 )

Pour revenir à mon problème initial, je vais continuer à creuser ce problème de communication inter VM et si je n’avance pas je vais revoir l’architecture. Peut-être persister les messages à envoyer: c’est moins performant mais ça peut aussi répondre à un besoin de suivi de diffusion.

La phase d’intégration d’un projet est toujours sous estimée J’ai vu des projets où cette phase était même plus importante en terme d’homme/jour que la phase de développement. Mais pour un hébergement dans le cloud il faut vraiment prendre cette problématique dès le début de la conception. C’est loin d’être un détail :)