179 lines
6.7 KiB
Markdown
Executable file
179 lines
6.7 KiB
Markdown
Executable file
<!-- title: Mes notes sur RabbitMQ -->
|
|
<!-- category: Développement -->
|
|
|
|
[RabbitMQ](https://www.rabbitmq.com) est un bus de messages Open Source qui
|
|
implémente le protocole Advanced Message Queuing (AMQP). Sa fonction est de
|
|
faire communiquer entre eux des programmes différents, potentiellement écrits
|
|
dans différents langages.<!-- more --> Le serveur RabbitMQ est lui-même écrit dans le
|
|
langage de programmation Erlang, ce qui est plutôt atypique. Aucune
|
|
connaissance de Erlang n'est nécessaire pour l'utiliser. C'est un produit
|
|
édité par Pivotal, un spin-off de VMWare et EMC, connu de tous les
|
|
développeurs JAVA pour son fabuleux framework
|
|
[Spring](https://en.wikipedia.org/wiki/Spring_Framework).
|
|
|
|
RabbitMQ demande une complexité de configuration proportionnelle aux exigences
|
|
demandées : queues de messages persistantes, dead letters, haute
|
|
disponibilité, optimisation des performances... Pour les configurations
|
|
compliquées et le support technique avancé dans des mises en oeuvre
|
|
d'entreprises, il y a des experts (j'ai une adresse pour ceux intéressés).
|
|
Pour une utilisation basique, dans un cadre de développement pépère à la
|
|
maison, RabbitMQ est très accessible, bien documenté et permet d'avoir
|
|
rapidement un bus de message pour faire communiquer ses applications.
|
|
|
|
Je m'en sers actuellement pour faire discuter mon petit éco-système hébergé. Dans ce cadre j'ai pris quelques notes sur sa mise en place, de l'installation à la configuration de base.
|
|
|
|
### Installation
|
|
|
|
Choix de l'OS : CentOS 7
|
|
|
|
Page d'aide de référence pour l'installation : https://www.rabbitmq.com/install-rpm.html
|
|
|
|
Pivotal fournit une installation d'une version allégée de Erlang avec les dépendances nécessaires à RabbitMQ : https://github.com/rabbitmq/erlang-rpm
|
|
|
|
Et bien sûr, il fournissent aussi un RPM de RabbitMQ (actuellement en version 3.7.2-1) : https://dl.bintray.com/rabbitmq/all/rabbitmq-server/3.7.2/rabbitmq-server-3.7.2-1.el7.noarch.rpm
|
|
|
|
Gestion du service à la sauce CentOS :
|
|
|
|
# démarrer le service
|
|
/sbin/service rabbitmq-server start
|
|
|
|
# stopper le service
|
|
/sbin/service rabbitmq-server stop
|
|
|
|
# démarrage automatique du service
|
|
chkconfig rabbitmq-server on
|
|
|
|
Après avoir mis le service en démarrage automatique, on n'utilisa plus que l'outil *rabbitmqctl* :
|
|
|
|
# démarrer le serveur rabbitmq
|
|
rabbitmqctl start_app
|
|
|
|
# stopper le serveur rabbitmq
|
|
rabbitmqctl stop_app
|
|
|
|
### Droits et permissions
|
|
|
|
Par défaut, un utilisateur *guest* (mot de passe idem) est créé et il est
|
|
attaché à l'interface réseau locale (localhost). Pour se connecter depuis une
|
|
autre machine, en distant, il faut créer un nouvel utilisateur.
|
|
|
|
rabbitmqctl add_user admin <mot de passe dur>
|
|
rabbitmqctl set_user_tags admin administrator
|
|
rabbitmqctl set_permissions -p / admin ".*" ".*" ".*"
|
|
|
|
### Interface Web d'administration
|
|
|
|
Une belle interface permet de gérer la configuration et de visualiser des
|
|
indicateurs de fonctionnement. C'est un plugin qu'on active en ligne de
|
|
commande avec *rabbitmq- plugins*
|
|
|
|
rabbitmq-plugins enable rabbitmq_management
|
|
|
|
L'interface Web répond à l'adresse *http://server-name:15672/cli/*
|
|
|
|
L'utilisateur *guest* n'a accès à l'interface que par localhost. Le nouveau compte admin est nécessaire pour se connecter en distant.
|
|
|
|
Si l'interface Web est derrière un proxy NginX et qu'elle répond à une sous-URL du domaine, la config pour pour une sous-url */rabbitwebmin* est la suivante :
|
|
|
|
location /rabbitwebmin/ {
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
rewrite ^/rabbitwebmin/(.*)$ /$1 break;
|
|
proxy_pass http://192.168.2.1:15672;
|
|
}
|
|
|
|
Optionnellement, on peut installer [le CLI de rabbitmqadmin](https://www.rabbitmq.com
|
|
/management-cli.html) en téléchargeant le programme (Python) depuis
|
|
https://raw.githubusercontent.com/rabbitmq/rabbitmq-
|
|
management/v3.7.2/bin/rabbitmqadmin et en le plaçant dans */usr/local/bin*.
|
|
|
|
### Mise en oeuvre
|
|
|
|
On crée un utilisateur technique pour nos applications dans un virtual host spécifique.
|
|
|
|
rabbitmqctl add_vhost devhub
|
|
rabbitmqctl add_user techuser tech
|
|
rabbitmqctl set_permissions -p devhub techuser ".*" ".*" ".*"
|
|
|
|
A ce niveau, on peut essayer de faire communiquer deux applications à travers Rabbit avec un classique producteur-consommateur écrit en Python, utilisant [la librairie Pika](https://pika.readthedocs.io), dérivé du tutorial de RabbitMQ.
|
|
|
|
Code du producteur :
|
|
|
|
```python
|
|
#!/usr/bin/env python
|
|
import pika
|
|
import sys
|
|
|
|
credentials = pika.PlainCredentials('techuser', 'tech')
|
|
connection = pika.BlockingConnection(pika.ConnectionParameters(host='192.168.2.1',credentials=credentials, virtual_host="devhub"))
|
|
channel = connection.channel()
|
|
|
|
channel.exchange_declare(exchange='hub.topic',
|
|
exchange_type='topic')
|
|
|
|
routing_key = sys.argv[1] if len(sys.argv) > 2 else 'anonymous.info'
|
|
message = ' '.join(sys.argv[2:]) or 'Hello World!'
|
|
channel.basic_publish(exchange='hub.topic',
|
|
routing_key=routing_key,
|
|
body=message)
|
|
print(" [x] Sent %r:%r" % (routing_key, message))
|
|
connection.close()
|
|
```
|
|
|
|
Code du consommateur :
|
|
|
|
```python
|
|
#!/usr/bin/env python
|
|
import pika
|
|
import sys
|
|
|
|
credentials = pika.PlainCredentials('techuser', 'tech')
|
|
connection = pika.BlockingConnection(pika.ConnectionParameters(host='192.168.2.1',credentials=credentials, virtual_host="devhub"))
|
|
channel = connection.channel()
|
|
|
|
channel.exchange_declare(exchange='hub.topic',
|
|
exchange_type='topic')
|
|
|
|
result = channel.queue_declare(exclusive=True)
|
|
queue_name = result.method.queue
|
|
print("Queue => " + queue_name)
|
|
|
|
# on s'abonne aux topics :
|
|
binding_keys = ['mail.message', 'mail.command.*']
|
|
|
|
for binding_key in binding_keys:
|
|
channel.queue_bind(exchange='hub.topic',
|
|
queue=queue_name,
|
|
routing_key=binding_key)
|
|
|
|
print(' [*] Waiting for logs. To exit press CTRL+C')
|
|
|
|
def callback(channel, method, properties, body):
|
|
print("=> %r:%r" % (method.routing_key, body))
|
|
|
|
channel.basic_consume(callback,
|
|
queue=queue_name,
|
|
no_ack=True)
|
|
|
|
channel.start_consuming()
|
|
```
|
|
|
|
Quelques tests :
|
|
|
|
# le consommateur lancé dans un shell s'abonne aux topics mail.message et mail.command.*
|
|
$ python3 consumer.py
|
|
|
|
le consommateur doit recevoir le message suivant :
|
|
|
|
# le producteur produit dans le topic 'mail.message'
|
|
$ python3 producer.py "mail.message"
|
|
|
|
le consommateur doit recevoir le message suivant :
|
|
|
|
# le producteur produit dans le topic 'mail.command.test'
|
|
$ python3 producer.py "mail.command.test"
|
|
|
|
le consommateur ne doit pas recevoir le message suivant :
|
|
|
|
# le producteur produit dans le topic 'mail.rate'
|
|
$ python3 producer.py "mail.rate"
|