## 回顧
在之前的教程中,我們改進了我們的日志系統 我們沒有使用只有虛擬廣播的扇出交換機,而是使用直接交換機,并有選擇性地接收日志的可能性。
## 需求
在我們的日志系統中,我們可能不僅要根據嚴重性來訂閱日志,還要根據發出日志的來源進行訂閱。你可能從syslog unix工具知道這個概念 ,它根據severity (info/warn/crit...) and facility (auth/cron/kern...).
這會給我們很大的靈活性 - 我們可能想要聽取來自'cron'的嚴重錯誤,而且還要聽取來自'kern'的所有日志。
### Topic交換
發送到主題交換的消息不能有任意的 routing_key - 它必須是由點分隔的單詞列表。單詞可以是任何東西,但通常它們指定連接到消息的一些功能。一些有效的路由鍵例子:"stock.usd.nyse","nyse.vmw ","quick.orange.rabbit"。在路由選擇鍵中可以有任意數量的字,最多255個字節。
當在綁定中不使用特殊字符“ * ”(星號)和“ # ”(散列)時,主題交換將像直接交換一樣。
綁定鍵也必須是相同的形式。主題交換背后的邏輯類似于direct。使用特定的路由密鑰發送的消息將被傳送到與匹配的綁定密鑰綁定的所有隊列。但是綁定鍵有兩個重要的特殊情況:
1. *(星號)可以代替一個單詞。
2. #(散列)可以代替零個或多個單詞。

**在這個例子中,我們將發送所有描述動物的信息。消息將使用由三個字(兩個點)組成的路由鍵發送。路由關鍵字中的第一個單詞將描述速度,第二個顏色和第三個種類**<speed>。<color>。<species>
我們創建了三個綁定:
1. Q1 綁定了綁定鍵"*.orange.*"
2. Q2 綁定了 "*.*.rabbit" and "lazy.#".
**概括**
Q1對所有的橙色動物感興趣。
Q2希望聽到有關兔子的一切,以及關于懶惰動物的一切。
**將路由鍵設置**
1. “ quick.orange.rabbit ”的消息將傳遞到兩個隊列。
2. “ lazy.orange.elephant ”也將去他們兩個。
3. “ quick.orange.fox ”只會到第一個隊列。
4. “ lazy.brown.fox ”只會到第二個隊列。即使匹配兩個綁定,“ lazy.pink.rabbit ”也只會被傳遞到第二個隊列一次。
5. “ quick.brown.fox ”不匹配任何綁定,因此將被丟棄。
6. “ lazy.orange.male.rabbit ”即使有四個單詞,也會匹配最后一個綁定,并被傳遞到第二個隊列。
### 代碼
**EmitLogTopic**
```
private static final String EXCHANGE_NAME = "topic_logs";
public static void main(String[] argv)
throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
String message = "topic";
String severity = "kern.critical";
channel.basicPublish(EXCHANGE_NAME, routingKey, null, message.getBytes());
System.out.println(" [x] Sent '" + routingKey + "':'" + message + "'");
connection.close();
}
```
**ReceiveLogsTopic**
```
public class ReceiveLogsTopic {
private static final String EXCHANGE_NAME = "topic_logs";
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.queueBind(queueName, EXCHANGE_NAME, "kern.*");
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
String queueName = channel.queueDeclare().getQueue();
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws UnsupportedEncodingException {
String message = new String(body, "UTF-8");
System.out.println(" [x] Received '" + envelope.getRoutingKey() + "':'" + message + "'");
}
};
channel.basicConsume(queueName, true, consumer);
}
}
```