RabbitMQチュートリアル(1) ～インストールからHello Worldまで(公式サイト翻訳)

オープンソースのMQの代表格であるRabbitMQを学習しています。学ぶにあたってとりあえず[公式サイトのチュートリアル](https://www.rabbitmq.com/getstarted.html)をこなしてみたので、自分の理解のためにも訳してみました。皆さんのお役に立てれば幸いです。

※ 公式サイトではさまざまなプログラム言語に対応していますが、とりあえずPythonでの例のみ訳しています。

## 前準備
この章は公式サイトのチュートリアルにはありませんが、インストールの手順をまずは書いておきます。

### RabbitMQのインストール
[公式サイトのドキュメント](https://www.rabbitmq.com/download.html)を確認のこと。以下はUbuntuの場合の手順です。

- 下記エントリを/etc/apt/sources.listに追記：

```shell-session
deb http://www.rabbitmq.com/debian/ testing main
```

- 公開鍵を登録

```shell-session
wget https://www.rabbitmq.com/rabbitmq-signing-key-public.asc
sudo apt-key add rabbitmq-signing-key-public.asc
```

- apt-get updateでパッケージリストを更新

```shell-session
sudo apt-get update
```

- インストール

```shell-session
sudo apt-get install rabbitmq-server
```

### Python用ライブラリ'pika'のインストール

pipでインストールできます。
```shell-session
sudo pip install pika
```
準備ができたら、チュートリアルに入りましょう。

# Tutorial 1: Hello World
※ 以下はRabbitMQ公式サイトの[Tutorial - Hello world! (python版)](https://www.rabbitmq.com/tutorials/tutorial-one-python.html)の翻訳です。意訳している部分もありますのでご了承ください。

## イントロダクション
Rabbit MQはメッセージブローカーです。主なアイデアは非常にシンブルで、「メッセージを受け取って、転送する」ことです。郵便局のようなものだと考えて良いでしょう：あなたが手紙をポストに投函するとき、あなたはいずれ郵便局の配達員が手紙を宛先へ届けてくれると確信しているでしょう。Rabbit MQの役割をこの例えに当てはめると、ポスト、郵便局、配達員すべての役割を担います。

RabbitMQと郵便局の違いは、扱うのが紙ではなくデータのかたまり、すなわち__メッセージ__であることです。Rabbit MQはこれを受け取り、保管し、転送します。

RabbitMQや一般的なメッセージングシステムでは、いくつかの専門用語を用います。

- _Producing_は送信を意味します。送信を行うプログラムは_Producer_と呼びます(以下、片仮名で"プロデューサ"と表記)。今後のチュートリアル内で登場するプログラムの解説図では、"P"という略称を使うことにします。
![producer.png](https://qiita-image-store.s3.amazonaws.com/0/33154/7377ff96-9a3b-0c11-c1f7-e28824ed6222.png)

- _queue_は、手紙を保管するメールボックスのようなものです(以下、片仮名で"キュー"と表記)。これはRabbit MQの内部で動作しています。あなたのアプリケーションがRabbit MQを介してメッセージを送る場合、それはただキューの中に格納されます。キューには制限なく好きなだけメッセージを格納させることができます - 本質的には無限のバッファであると言えます。複数のプロデューサから一つのキューにメッセージを送ることができ、また複数のコンシューマが一つのキューからメッセージを受け取ることができます。今後のチュートリアル内の解説図では、キュー名を上に付加した次のようなイラストを用います。
![queue.png](https://qiita-image-store.s3.amazonaws.com/0/33154/e4f26d32-cd36-4830-723b-28faeb48515a.png)

- _Consuming_は、受信に近い意味です。メッセージの受信を待つプログラムを_Consumer_と呼びます(以下、片仮名で"コンシューマ"と表記)。 チュートリアル内の解説図では、"C"という略称を使うことにします。
![consumer.png](https://qiita-image-store.s3.amazonaws.com/0/33154/8a0fb977-99b1-6871-0f45-61954ea83e3a.png)

プロデューサ、コンシューマ、ブローカ（RabbitMQサーバ）は同じマシンに同居させる必要はない、ということを覚えてください。実際、多くのアプリケーションではこれらを分離して配置する構成を取ります。

## Hello World!

このHello Worldプログラムレイは大して複雑ではありません。メッセージを送信して、それをスクリーンに出力してみましょう。実現には、二つのプログラムが必要です：一つはメッセージを送信するプログラム、もう一つはそのメッセージを受信して出力するプログラムです。

全体のデザインは以下のようなものです：
![python-one-overall.png](https://qiita-image-store.s3.amazonaws.com/0/33154/b07f2d05-cc78-2e70-46b0-0fbc010fdcb6.png)

プロデューサ(P)は"Hello"キューにメッセージを送ります。コンシューマ(C)はキューからメッセージを受け取ります。

>__RabbitMQのライブラリについて：__
>RabbitMQはオープンで汎用的なメッセージングプロトコルであるAMQP0.9.1を通信に利用します。RabbitMQに対応したクライアントはさまざまな言語で用意されています。このチュートリアルで利用するpikaは、RabbitMQチームの推奨するPythonクライアントです。

### 送信
![sending.png](https://qiita-image-store.s3.amazonaws.com/0/33154/091fc39d-2552-d462-20bd-f9b320d286cf.png)
送信プログラムsend.pyを作成します。<a href=#send_py>(コード全文)</a>
最初に行うことは、RabbitMQサーバとの接続を確立することです：

```py
#!/usr/bin/env python
import pika

connection = pika.BlockingConnection(pika.ConnectionParameters(
               'localhost'))
channel = connection.channel()
```

この例ではローカルホストのブローカ（RabbitMQサーバ）に接続しています。外部のマシンのブローカに接続したい場合は、単純に`localhost`の部分にIPアドレスもしくはホスト名を指定します。

メッセージの送信を行う際には、宛先のキューがすでに存在している必要があります。もし存在しないキューにメッセージを送ってしまうと、RabbitMQはそのメッセージを捨ててしまいます。今回は、`Hello`という名前のキューに送ることとして、このキューを作成しましょう：

```py
channel.queue_declare(queue='hello')
```

これでメッセージを送る準備ができました。`Hello World!`という単語だけを含んだメッセージを、`Hello`キューに送ろうと思います。

RabbitMQでは、メッセージはキューに直接送られるのではなく、必ずexchangeというコンポーネントを介して送られるのですが、その詳細までは今は掘り下げません（チュートリアル3で解説します）。とりあえず、空文字(`''`)を指定することによって、デフォルトのexchangeを利用する方法を学びましょう。このexchangeは特殊で、どのキューにメッセージを送るかを直接指定することができます。送信先のキューの名前は、`routing_key`パラメータに指定します：

```py
channel.basic_publish(exchange='',
                      routing_key='hello',
                      body='Hello World!')
print(" [x] Sent 'Hello World!'")
```

プログラムを抜ける前に、ネットワークバッファがフラッシュされ、メッセージがきちんとRabbitMQへ送られたかを確かめる必要があります。下記のコードで、それらの処理を行ってコネクションを閉じることができます。
```py
connection.close()
```


>__送信がうまくいかない__
>もしこれが初めてのRabbitMQの利用で、メッセージの送信がうまくいかなければ、なにが間違っているのか頭を掻いて悩むところでしょう。もしかしたら、ブローカ（RabbitMQサーバ）が、充分なディスクスペースを確保できずに起動しているのかもしれません（最低で1GBのスペースが必要です）。この場合RabbitMQはメッセージを受け付けません。ログファイルを確認して、必要であれば最低限必要なディスクスペースの制限の設定（`disk_free_limit`）の値を下げてください。[configuration file documentation](http://www.rabbitmq.com/configure.html#config-items)に設定方法が記載されています。

### 受信
![receiving.png](https://qiita-image-store.s3.amazonaws.com/0/33154/2f4caead-7e8b-4179-8a00-99ddf158405f.png)
二つ目のプログラムreceive.pyは、メッセージをキューから受け取ってスクリーンに出力します。<a href=#receive_py>(コード全文)</a>

まずRabbitMQサーバに接続します。このコードはsend.pyと同様です。

次に、利用するキューが確実に存在するように、`queue_declare`を呼び出します。このメソッドはべき等性を保証していて、すでに指定したキューが存在していれば何もしません。そのため何度でも好きなだけ呼び出せます。（同じ名前のキューは一つしか作成されません）

```py
channel.queue_declare(queue='hello')
```

なぜsend.pyのコード内で記述したキューの宣言をまた行うのか、と疑問に思うかもしれません。理由はキューが存在していない、という事態を確実に避けるためです。send.pyを必ず先に立ち上げるのであれば、（send.pyだけでキューの宣言しておけば）問題ありませんが、我々はsend.pyとreceive.py、どちらを先に立ち上げるか決めていません。こうした場合、両方のプログラムにキューの宣言（`queue_declare`）を記述するのは良いプラクティスです。

>__キューのリスティング__
>RabbitMQにいくつのキューがあって、どれくらいのメッセージがあるのかを確認したい場合があるでしょう。そうした場合には、`rabbitmqctl`ツールを権限のあるユーザで実行します：
>
>```shell-session
$ sudo rabbitmqctl list_queues
Listing queues ...
hello    0
...done.
>```

キューからのメッセージの受信は少し複雑です。`callback`関数をキューに登録することで、処理を動かすことができます。メッセージを受信したら、いつでも`callback`関数がPikaライブラリから呼び出されます。今回は、この関数ではメッセージの中身をスクリーンに出力するようにします。

```py
def callback(ch, method, properties, body):
    print(" [x] Received %r" % body)
```

次に、RabbitMQに対して、`hello`からメッセージを受け取った際にこのcallbackを呼び出すよう伝えます：

```py
channel.basic_consume(callback,
                      queue='hello',
                      no_ack=True)
```

このコマンドは、登録を行うキューが存在しなければ成功しません。幸い、我々は`queue_declare`をこの前に記述しているため、キューが存在することに確信が持てます。
`no_ack`パラメータについては、チュートリアル2で説明します。
最後に、データを待ち受けてコールバックを動かす無限ループに入ります：

```py
print(' [*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()
```

### 全体のコード

<a name="send_py"></a>

```py:send.py
#!/usr/bin/env python
import pika

connection = pika.BlockingConnection(pika.ConnectionParameters(
        host='localhost'))
channel = connection.channel()

channel.queue_declare(queue='hello')

channel.basic_publish(exchange='',
                      routing_key='hello',
                      body='Hello World!')
print(" [x] Sent 'Hello World!'")
connection.close()
```

<a name="receive_py"></a>

```py:receive.py
#!/usr/bin/env python
import pika

connection = pika.BlockingConnection(pika.ConnectionParameters(
        host='localhost'))
channel = connection.channel()

channel.queue_declare(queue='hello')

def callback(ch, method, properties, body):
    print(" [x] Received %r" % body)

channel.basic_consume(callback,
                      queue='hello',
                      no_ack=True)

print(' [*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()
```

ターミナルから、プログラムを試してみましょう。まずはsend.pyを動かしてメッセージを送ります：

```shell-session
 $ python send.py
 [x] Sent 'Hello World!'
```

プロデューサプログラムであるsend.pyは送信を行うとすぐに終了します。次はメッセージを受信してみましょう:

```shell-session
 $ python receive.py
 [*] Waiting for messages. To exit press CTRL+C
 [x] Received 'Hello World!'
```

万歳! RabbitMQを利用した最初のメッセージの送受信に成功しました。お気づきのように、reveive.pyはメッセージを受け取っても終了せず、また次のメッセージを待ち受けます。終了させるにはCtrl-Cを押してください。

今回のチュートリアルで、我々は名前付きキューを利用したメッセージの送受信の方法を学びました。チュートリアル2では、シングルなWorkキューの作成を行います。