RabbitMQ チュートリアル５（トピック）

RabbitMQのチュートリアル５
https://www.rabbitmq.com/tutorials/tutorial-five-python.html
の翻訳です。
翻訳の誤りなどあればご指摘お待ちしております。

###前提条件

このチュートリアルでは、RabbitMQのがインストールされ、ローカルホストの標準のポート（5672）上で実行されている前提とします。別のホスト、ポート、または資格情報を使用する場合には、接続設定の調整が必要です。

###問題が発生した場合

このチュートリアルを通して問題が発生した場合、メーリングリストを通して私たちに連絡することができます。

# トピック
#### （pika 0.9.8 Python clientを使用）

前のチュートリアルでは、ロギング・システムを改善しました。ダミーのブロードキャストしかできないファンアウト・エクスチェンジのかわりに、ダイレクト・エクスチェンジを使用し、選択的にログを受信することが可能になりました。

ダイレクト・エクスチェンジを使用してシステムを改善しましたが、まだ制限があります。ダイレクト・エクスチェンジでは、複数の基準に基づいてルーティングを行うことができません。

私たちのロギング・システムでは、重大度に基づくだけでなく、ログを発行するソースに基づいて、サブスクライブしたいです。あなたは unix の syslog ツールにより、この概念を知っているかもしれません、それは重大度（情報/警告/クリティカル...）やファシリティ（auth/cron/kern...）に基づいて、ログをルーティングします。

このツールはとても柔軟性です、「cron」から来る重大なエラーだけでなく、「kern」から来るすべてのログを取得したいことがあります。

私たちのロギング・システムでこれを実現するために、より複雑なトピック・エクスチェンジについて学ぶ必要があります。

# トピック・エクスチェンジ

トピック・エクスチェンジに送信されたメッセージは、任意の routing_key を持つことはできません、それはドットで区切られた単語のリストでなければなりません。その単語は任意ですが、通常、メッセージに関連するいくつかの特徴を指定します。ルーティング・キーの例："stock.usd.nyse"、"nyse.vmw"、"quick.orange.rabbit"。255バイトの限界まで、ルーティング・キーに任意の数の単語を含めることができます。

バインディング・キーは同じ形式でなければなりません。トピック・エクスチェンジのロジックは、ダイレクト・エクスチェンジと同様です、特定のルーティング・キーと共に送信されたメッセージは、一致するバインディング・キーで結合されているすべてのキューに配信されます。ただし、バインディング・キーについて、２つの重要な特殊ケースがあります：

\*（スター）は、ちょうど１つの単語を置き換えることができます。
\#（ハッシュ）は、ゼロ個以上の単語を置き換えることができます。

例で説明するのが一番簡単です：

（図）

この例では、動物を表すメッセージを送信します。メッセージは、次の3つの単語（と２つのドット）で構成されたルーティング・キーで送信されます。ルーティング・キーの最初の単語が「機敏さ」、２番目の単語が「色」、三番目の単語が「種」を説明します："(機敏さ).(色).(種)"

３つのバインディングを作成します、"Q1"は"\*.orange\*"で結合され、"Q2"は"\*.\*.rabbit"および"lazy.#"で結合されます。

これらのバインディングは、次のように要約することができます：

"Q1"は、すべてのオレンジ色の動物に関心がある。
"Q2"はウサギに関するあらゆること、および怠惰な動物についてのあらゆることを聞きたい。

ルーティング・キーが"quick.orange.rabbit"と設定されたメッセージは、両方のキューに配信されます。メッセージ "lazy.orange.elephant"もまた、両方に行きます。一方、"quick.orange.fox"は最初のキューにのみ行き、"lazy.brown.fox"は２番目にのみ行きます。"lazy.pink.rabbit"は、２番目のキューの２つのバインディングと一致しますが、２番目のキューに一度だけ配信されます。"quick.brown.fox"はどのバインディングとも一致しないため、破棄されます。

約束を破り、単一の単語や４つの単語を伴うメッセージ、例えば"orange"や"quick.orange.male.rabbit"など、を送信した場合はどうなるでしょうか？これらのメッセージは、どのバインディングとも一致しないため、失われます。

一方、"lazy.orange.male.rabbit"は、４つの単語を持っていますが、最後のバインディングと一致するため、２番目のキューに配信されます。

```
トピック・エクスチェンジ

トピック・エクスチェンジは強力であり、他のエクスチェンジのように振る舞うことができます。

キューがバインディング・キー「#」（ハッシュ）に結合されている場合、ファンアウト・エクスチェンジのように、ルーティング・キーにかかわらず、すべてのメッセージを受信します。    

特殊文字「*」（スター）、「#」（ハッシュ）がバインディングに使用されていない場合には、トピック・エクスチェンジはまさにダイレクト・エクスチェンジのように動作します。
```

# すべてのまとめ

ロギング・システムではトピック・エクスチェンジを使用します。ログのルーティング・キーが２つの単語を持っていると仮定して始めましょう："(ファシリティ).(重大度)"。

コードはほぼ前のチュートリアルと同じです。

emit_log_topic.pyのコード

```py
 1    #!/usr/bin/env python
 2    import pika
 3    import sys
 4
 5    connection = pika.BlockingConnection(pika.ConnectionParameters(
 6            host='localhost'))
 7    channel = connection.channel()
 8
 9    channel.exchange_declare(exchange='topic_logs',
10                             type='topic')
11
12    routing_key = sys.argv[1] if len(sys.argv) > 1 else 'anonymous.info'
13    message = ' '.join(sys.argv[2:]) or 'Hello World!'
14    channel.basic_publish(exchange='topic_logs',
15                          routing_key=routing_key,
16                          body=message)
17    print " [x] Sent %r:%r" % (routing_key, message)
18    connection.close()
```

receive_logs_topic.pyのコード：

```py
 1    #!/usr/bin/env python
 2    import pika
 3    import sys
 4
 5    connection = pika.BlockingConnection(pika.ConnectionParameters(
 6            host='localhost'))
 7    channel = connection.channel()
 8
 9    channel.exchange_declare(exchange='topic_logs',
10                             type='topic')
11
12    result = channel.queue_declare(exclusive=True)
13    queue_name = result.method.queue
14
15    binding_keys = sys.argv[1:]
16    if not binding_keys:
17        print >> sys.stderr, "Usage: %s [binding_key]..." % (sys.argv[0],)
18        sys.exit(1)
19
20    for binding_key in binding_keys:
21        channel.queue_bind(exchange='topic_logs',
22                           queue=queue_name,
23                           routing_key=binding_key)
24
25    print ' [*] Waiting for logs. To exit press CTRL+C'
26
27    def callback(ch, method, properties, body):
28        print " [x] %r:%r" % (method.routing_key, body,)
29
30    channel.basic_consume(callback,
31                          queue=queue_name,
32                          no_ack=True)
33
34    channel.start_consuming()
```

すべてのログを受信するには：

```
python receive_logs_topic.py "#"
```

ファシリティ"kern"からのすべてのログを受信するには：

```
python receive_logs_topic.py "kern.*"
```

"critical"のログのみ聞きたい場合は：

```
python receive_logs_topic.py "*.critical"
```

複数のバインディングを生成できます：

```
python receive_logs_topic.py "kern.*" "*.critical"
```

ルーティング・キー"kern.critical"のログを発行するには：

```
python emit_log_topic.py "kern.critical" "A critical kernel error"
```

このプログラムで遊んでみてください。コードがルーティングまたはバインディング・キーについて仮定していないことに注意してください、２つ以上のルーティング・キーのパラメータで実行することができます。

いくつかの難題：

"\*"のバインディングは空のルーティング・キーで送信されたメッセージを捕捉するでしょうか？
"#.\*"は、文字列".."をキーとするメッセージを捕捉しますか？単一の単語のキーのメッセージを捕捉しますか？
"a.\*.#"と"a.#"との違いは？

RPCについて学ぶためにチュートリアル６に移ります。