Archive for January, 2008

Cet article a pour but de décrire l’architecture employée derrière le site de micro-blogging Noumba. Il n’a pas pour objectif d’être exhaustif ni de prétendre proposer la solution idéale, mais seulement présenter un moyen de résoudre certaines contraintes selon notre contexte.

Micro-blogging

Le micro-blogging est un concept, lancé par Twitter, qui permet de s’exprimer en de courtes phrases, tenant en une centaine de caractères de manière à pouvoir être lu depuis un téléphone portable, par SMS ou WAP.
En une phrase on ne communique le même type d’information que sur son blog. Cela va donc de son humeur, un lien, ou une rapide information qui ne nécessite pas un long exposé. Ce type d’information sont plus courante que celle que l’on écrirait sur son blog, et à ce titre le micro-blogging est un complément au blog plus qu’une alternative.

Le corollaire est la possibilité de s’exprimer depuis son téléphone portable vers le site.

Au final un site de micro-blogging est souvent utilisé en tant que passerelle entre 2 outils de communication (Phone2Phone, Desktop2API, Phone2Jabber, Web2…).

Contexte

Noumba est un site web de micro-blogging en Ruby on Rails. Il est ouvert à tous, mais son orientation commerciale le prédispose à une certaine tranche d’âge d’utilisateurs, entre 13 et 25 ans. De fait il est pour l’instant plus utilisé pour du chat que du micro-blogging, ce qui génère un fort nombre de messages par utilisateur.

En outre de part le profil des utilisateurs, des filtres sont indispensable afin de garantir une certaine qualité de contenu et de confidentialité.

Pour le reste, les contraintes sont les mêmes que des sites tels que Twitter ou Jaiku, cependant Noumba a ceci de particulier qu’il propose un certain nombre de satellites utilisés par des partenaires, tel que SFR (sfr.noumba.net), NRJ, (mikl.noumba.net, 6-9.noumba.net) MyMajorCompany (mymajorcompany.noumba.net), MCM (mcm.noumba.net), …

Contraintes

Des traitements asynchrones

Lorsqu’un message est posté, des traitements sont appliqués (filtrage, routage, emails, logs,…). Ces traitements ne doivent pas augmenter la latence entre l’utilisateur et le site web. Il n’est donc pas possible de les effectuer de manière synchrone, c’est à dire imposer à l’utilisateur d’attendre leur fin avant de lui rendre la main.
Si un fonctionnement synchrone est envisageable avec quelques utilisateurs en simultané, il est tout bonnement impossible pour un site à vocation grand public si l’on veut respecter une certaine qualité de service.

La solution est d’effectuer ces traitements de manière asynchrone, c’est à dire en parallèle, sans imposer à l’utilisateur leur fin pour lui rendre la main.

Les solutions

background

La première solution utilisée sur Noumba fut backgroundrb. Cet outil fournit un serveur de tâche permettant de les paralléliser dans un processus.
Si cette solution est intéressante pour quelques tâches simples, elle n’est plus possible dans le contexte lourd d’un micro-blogging, d’autant plus que backgroundrb utilisait les threads.
Les threads en Ruby sont peu efficaces (green thread) et une telle architecture consomme trop de ressources et est l’origine de bugs difficilement décelables.
Ces threads permettent d’exécuter les traitements, workers, cependant la communication entre eux n’est pas aisée ce qui complique l’enchainement des traitements.

Backgroundrb a depuis été repris et n’utilise plus à priori les threads. Malgré tout, une architecture robuste à base de file d’attente est préférable.

Hey MoM ! ou le monde des messages brokers

Les Message oriented Middleware sont des architectures qui permettent de faire communiquer des applicatifs de manière asynchrone par le biais d’une file d’attente. JMS est le MoM le plus connu et un certain nombre d’implémentations libres existent (ActiveMQ, RabbitMQ)

Parmis ceux-ci, certains implémentent le protocole texte stomp, plus simple et adapté à un site de micro-blogging. StompServer en Ruby en fait parti.

Plus de détails sur les MoM

ActiveMessaging.

Ce plugin Rails permet de communiquer vers un serveur MoM. Il lance un poller qui interroge le serveur MoM afin de lui transmettre ou lui retirer les messages.
Ce plugin est MoM agnostique, on peut interroger aussi bien un serveur ActiveMQ, StompServer et même AmazonSQS.

Noumba a utilisé un certain temps un backend StompServer / ActiveMessaging. C’est une architecture intéressante cependant l’évolution et la qualité du code de ActiveMessaging sont à surveiller de prêt.

Noumba en tant que micro-blog génère une forte charge. Or cette charge devient vite un handicap pour le bon fonctionnement du poller.
La conclusion qui s’impose est que Noumba se doit d’être le plus léger possible en effectuant le moins d’opération même par le biais d’un plugin.

Que REST il ?

REST est en effet la solution la plus simple, la plus standard (HTTP) et la moins lourde.
Dans sa dernière architecture, Noumba communique en REST avec un backend, le Hub, également en Rails. Les messages sous forme de Hash sont sérialisés puis transmis au backend.
Cependant il est toujours nécessaire d’implémenter une architecture MoM cà´té Hub afin d’obtenir un fonctionnement asynchrones.

XMPP bien sûr !

Quel serveur robuste fonctionne de fait sous forme de file d’attente, tout en étant robuste et utilisant un protocole standard, connu et ouvert ? XMPP bien sûr !
Ejabberd est un des serveurs Jabber le plus robuste. Etant en erlang, il peut facilement être réparti sous forme de cluster si le besoin se fait sentir.

Entre REST et XMPP il nous manque le lien Ruby on Rails. ActionMessenger vient le combler. Ce plugin Rails permet de transmettre des messages XMPP en utilisant un compte Jabber. Il fonctionne simplement à la manière d’ActionMailer.

Le Hub implémente ce plugin. Il reçoit les objets sérialisés et les transmet à divers comptes Jabber puis répond aussità´t à Noumba au travers de son API REST.

Click to continue reading

L'architecture.

Les bots

Il ne reste qu'à exploiter les capacités de Rails et de XMPP afin de développer les bots qui vont effectuer les traitements.

Exemple :

RUBY:
  1. #!/usr/bin/env ruby
  2. #
  3. # MonBot save message through ActiveRecord
  4.  
  5. require 'rubygems'
  6. require 'xmpp4r-simple'
  7. require 'daemons'
  8. require 'yaml'
  9. require 'logger'
  10.  
  11. RAILS_ENV = ARGV[0] || 'development'
  12. require File.dirname(__FILE__) + '/../config/environment'
  13.  
  14. require 'mysql_retry_lost_connection'
  15.  
  16. class MonBot
  17. @@bot_jid = "monbot@jabber.toto.com/1"
  18. @@bot_password = '123'
  19. @@logger = Logger.new("monbot.log")
  20.  
  21. def initialize
  22. @@logger.info('initialize') { "Initializing in #{RAILS_ENV} mode ..." }
  23. @jabber = Jabber::Simple.new(@@bot_jid, @@bot_password)
  24. end
  25.  
  26. def receive_msg
  27. loop do
  28. @jabber.received_messages do |message|
  29.  
  30. # on désérialise le message s'il a été transmis de la sorte
  31. obj = YAML.load(message.body)
  32.  
  33. mon_traitement(obj)
  34.  
  35. end
  36. sleep 0.5
  37. end
  38. end
  39.  
  40. private
  41.  
  42. def mon_traitement(obj)
  43. obj.find_by_login("toto")
  44. end
  45.  
  46. end
  47.  
  48. bot = MonBot.new
  49. bot.receive_msg

RUBY:
  1. RAILS_ENV = ARGV[0]
  2. require File.dirname(__FILE__) + '/../config/environment'

Ces lignes permettent à un script Ruby de charger l'environnement Rails du projet. Le bot est alors capable d'attaquer notre modèle de données via ActiveRecord.

RUBY:
  1. require 'xmpp4r-simple'

Ce gem nous permet de communiquer vers le compte Jabber du bot. Il peut donc dépiler les objets qui lui sont destiné et les désérialiser pour les traiter.

RUBY:
  1. require 'daemons'

Ce gem permet de gérer le bot en tant que service (stop/start/restart).

RUBY:
  1. require 'yaml'

Pour sérialiser/désérialiser vos objets Ruby / Rails.

RUBY:
  1. require 'mysql_retry_lost_connection'

Ce gem sert à intercepter une coupure de la connexion vers MySQL. Il surcharge ActiveRecord afin de renégocier une connexion.

Avec ces outils nous avons la capacité de créer des services exploitant une file d'attente Jabber et fonctionnant en parallèle. Ainsi, si la charge vient à augmenter, les messages en attente de traitement ne seront pas perdus, puisqu'en attente dans les comptes Jabber stockés par ejabberd. De plus il est possible de multiplier un même bot en exploitant les ressources du protocole Jabber, chaque bot écoutant sur sa propre ressource (bot@monserveurjabber.com/1, bot@monserveurjabber.com/2, ...).

Résumé

Pour Noumba, nous avons développé un projet en Rails, le Hub, qui gère la file d'attente Jabber, et communique en REST avec le frontal Noumba. Si cette architecture est sur-dimensionnée pour votre projet, nul besoin d'un backend. Votre site et des bots suffisent amplement. De même quelques comptes Gmail suffisent si vos ne souhaitez pas déployer votre propre serveur Jabber.

Si un backend en Rails s'avère nécessaire il n'est pas conseillé de dupliquer les modèles de votre site principal vers celui-ci. Cela fonctionne mais le principe DRY est de fait supprimé. Cependant un outil tel que Piston permet de temporiser cette affirmation.

Les autres

Après divers essais Twitter a fini par développer son propre serveur de file qui exploite memcached, starling

Un développeur de Seesmic indique utiliser ActiveMQ et RabbitMQ mais semble vouloir migrer vers une solution XMPP : scaling-questions-and-issues

Il existe un grand nombre d'alternatives, on peut citer :

Les deux premiers utilisent le twisted like eventmachine

Bémol

Une telle architecture implique la gestion d'un serveur Jabber, ce qui peut s'avérer une tâche plus complexe et lourde qu'un réel MoM dédié. De plus la stabilité de la bibliothèque xmpp4r ainsi que le plugin ActionMessenger est à surveiller de près. Pour ce dernier, il a été nécessaire de le patcher afin qu'il puisse supporter plusieurs instances Rails.

Avenir

XMPP est un protocole standard et ouvert très répandu ce qui en fait un candidat idéal si l'on souhaite une architecture pérenne et évolutive. L'architecture décentralisée de Jabber et ses capacités à se connecter à des services externes tel que OpenID (xmppid.net) ouvre la porte à une multitude de possibilités.

Ses nombreuses fonctionnalités dédiés au chat (room, pub/sub, voIP, ...) sont toutes indiquées pour des sites communautaires et sociaux à tel point que le projet DiSo souhaite l'utiliser en son coeur, mais ceci est un autre sujet.

Click to continue reading

Creative Commons License
This work is licensed under a Creative Commons Attribution 2.0 License.