RabbitMQ 之五 Topics

在上篇文章Routing中,我們改進了日誌系統,使用類型爲direct的exchange,使得可以有選擇性的接收日誌。而不是fanout那樣只是簡單的廣播信息。

雖然使用了direct的exchange來改進我們的日誌系統,但是還是有侷限性,無法根據多重條件來進行路由選擇。

在我們的日誌系統中,我們希望不僅僅根據日誌的級別,而且根據日誌的來源來訂閱日誌。類似於syslog這個unix工具,它根據嚴重性(info/warn/crit...)和設備(auth/cron/kern...)來選擇發送日誌。

這樣可能給予我們更多的靈活性,我們希望關注來自於“cron”的critical errors和來自於“kern”的所有日誌。

發往topic類型exchange的消息不能任意選擇routing_key,必須是由.隔開的一系列標識,這些標識可以是任何東西,但經常是一些與消息特性相關的詞,一些合法的routing key的例子:"stock.usd.nyse","nyse.vmw","quick.orange.rabbit"。routing key的長度限制爲255bytes。

綁定的key和routing key的形式一樣,topic exchange的邏輯跟direct exchange類似。附帶特殊routing key的消息會被分發到所有綁定匹配的binding key的queue中。需要注意的是,關於binding key有兩個特殊的情況。

* 可以匹配一個標識。

#可以匹配0個或多個標識。

上圖所示,我們準備發送關於動物的消息,消息會附帶一個routing key包含3個標識(兩個.隔開),第一個標識描述速度,第二個標識描述顏色,第三個標識描述種類:"<speed>.<colour>.<species>"。

我們建了3個綁定關係。Q1通過"*.orange.*"綁定,Q2通過"*.*.rabbit"和"lazy.#"來綁定。

這幾個綁定關係可以總結爲:

Q1對所有橙色動物感興趣。

Q2想要知道關於兔子的一切以及關於懶洋洋的動物的一切

消息附帶routing key爲"quick.orange.rabbit"將會被分發到Q1和Q2。消息附帶routing key爲"lazy.orange.elephant"也會被分發到Q1和Q2。消息附帶routing key爲"quick.orange.fox"只會被分發到Q1。消息附帶routing key爲"lazy.brown.fox"會被分發到Q2。消息附帶routing key爲"lazy.pink.rabbit"將會被分發一次到Q2,雖然這個消息匹配到兩個binding key。消息附帶routing key爲"quick.brown.fox"將會被丟棄。

如果我們違法我們的約定,發送一個或者四個標識符的選擇鍵,類似:orange,quick.orange.male.rabbit,這些選擇鍵不能與任何綁定鍵匹配,所以消息將會被丟棄。

代碼如下:EmitLogTopic.java

package org.rabbitmq;

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

public class EmitLogTopic {
	private static final String EXCHANGE_NAME = "topic_logs";

	public static void main(String[] argv) {
		Connection connection = null;
		Channel channel = null;
		try {
			ConnectionFactory factory = new ConnectionFactory();
			factory.setHost("localhost");
			factory.setPort(5673);
			
			connection = factory.newConnection();
			channel = connection.createChannel();

			channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
			String regArgv[] = new String[]{"kern.critical","A critical kernel error"};
			String routingKey = getRouting(regArgv);
			String message = getMessage(regArgv);

			channel.basicPublish(EXCHANGE_NAME, routingKey, null, message.getBytes("UTF-8"));
			System.out.println(" [x] Sent '" + routingKey + "':'" + message + "'");

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (connection != null) {
				try {
					connection.close();
				} catch (Exception ignore) {
				}
			}
		}
	}

	private static String getRouting(String[] strings) {
		if (strings.length < 1)
			return "anonymous.info";
		return strings[0];
	}

	private static String getMessage(String[] strings) {
		if (strings.length < 2)
			return "Hello World!";
		return joinStrings(strings, " ", 1);
	}

	private static String joinStrings(String[] strings, String delimiter, int startIndex) {
		int length = strings.length;
		if (length == 0)
			return "";
		if (length < startIndex)
			return "";
		StringBuilder words = new StringBuilder(strings[startIndex]);
		for (int i = startIndex + 1; i < length; i++) {
			words.append(delimiter).append(strings[i]);
		}
		return words.toString();
	}
}
ReceiveLogsTopic.java

package org.rabbitmq;

import java.io.IOException;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

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");
		factory.setPort(5673);
		
		Connection connection = factory.newConnection();
		Channel channel = connection.createChannel();

		channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
		String queueName = channel.queueDeclare().getQueue();
		String regArgv[] = new String[]{"kern.*","*.critical"};
		if (regArgv.length < 1) {
			System.err.println("Usage: ReceiveLogsTopic [binding_key]...");
			System.exit(1);
		}

		for (String bindingKey : regArgv) {
			channel.queueBind(queueName, EXCHANGE_NAME, bindingKey);
		}

		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 IOException {
				String message = new String(body, "UTF-8");
				System.out.println(" [x] Received '" + envelope.getRoutingKey() + "':'" + message + "'");
			}
		};
		channel.basicConsume(queueName, true, consumer);
	}
}

運行結果如下:

參考http://www.rabbitmq.com/tutorials/tutorial-five-java.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章