###主题(topic) ###(使用Java客户端) 在先前的指南中咱们改进了咱们的日志系统。取代使用fanout
类型的交易所,那个仅仅有能力实现哑的广播,咱们使用一个direct
类型的交易所,得到一个能够有选择性的接收日志。java
虽然使用direct
交易所类型已经改善了咱们的系统,但它依旧有限制-它不能根据多个条件进行路由。python
咱们的日志系统中,咱们可能想要订阅不单单基于严格的日志,一样基于发布日志的源码。你可能了解到syslog unix tool
的概念,那个基于严格的(info/warn/crit...)和灵巧的(auth/cron/kern...)路由日志。git
那个将会给咱们许多灵活性-咱们可能仅仅想监听来自于cron
的关键性的错误和全部来自于kern
的日志。github
为了在咱们日志系统中实现那个,咱们须要学习更复杂的topic
类型交易所。shell
###topic
类型交易所 发送到topic
类型的交易所不能有任意的路由的关键字-它必须是一个关键字列表,由点分隔。这关键字能够是任意的,可是一般能够说明消息的基本的联系。几个合法的路由关键字例子:"stock.usd.nyse","nyse.vmw","quick.orange.rabbit"。可能有不少你想要的路由关键字,上限是255个字节。 这绑定关键字必须也在这一样的表单里。topic
交易所逻辑背后是与direct
交易所类型相似-一个带特别的路由关键字的消息将会被传递到全部匹配绑定的关键字的队列。可是有两个特别重要的绑定关键字。windows
>* (星标) 能替代任意一个单词。 ># (哈希) 能代替零个或多个单词。学习
这个例子中是很容易解释的: ui
在这个例子中,咱们发送的消息都描述的是动物。被发送的消息的路由关键字是由三个单词(两个点)组成。路由关键字中第一个单词描述的是速度,第二个描述的是颜色,第三个是物种: "<速度>.<颜色>.<物种>"。unix
咱们建立了三个绑定:Q1s是由绑定关键字"。orange."所约束,Q2由"。。rabbit"和"lazy.#"所约束。 这些绑定能够归纳为:日志
>Q1 是对
orange
颜色的动物感兴趣。 >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",即便它有四个单词,将会匹配最最后那个绑定,将消息传递到第二个队列。
topic
类型交易所topic
类型交易所是强大的,能表现的像其余的交易所。 Topic exchange is powerful and can behave like other exchanges. 当一个队列绑定到了"#"(哈希)绑定关键字-它会接收全部消息,无论路由关键字是什么-相似于fanout
类型交易所 当topic
类型交易所中没有使用像"*"(星标)和"#"(哈希)的特殊字符,它的行为相似于direct
类型交易所
###把全部放在一块儿 咱们将会在咱们的日志系统中使用topic
类型交易所。咱们假设咱们的工做的日志消息的路由关键字是由两个单词组成,格式为:"<facility>.<severity>"。 这代码和先前的几乎同样: EmitLogTopic.java
的代码:
public class 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 routingKey = getRouting(argv); String message = getMessage(argv); channel.basicPublish(EXCHANGE_NAME, routingKey, null, message.getBytes()); System.out.println(" [x] Sent '" + routingKey + "':'" + message + "'"); connection.close(); } //... }
ReceiveLogsTopic.java
的代码:
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.exchangeDeclare(EXCHANGE_NAME, "topic"); String queueName = channel.queueDeclare().getQueue(); if (argv.length < 1){ System.err.println("Usage: ReceiveLogsTopic [binding_key]..."); System.exit(1); } for(String bindingKey : argv){ channel.queueBind(queueName, EXCHANGE_NAME, bindingKey); } System.out.println(" [*] Waiting for messages. To exit press CTRL+C"); QueueingConsumer consumer = new QueueingConsumer(channel); channel.basicConsume(queueName, true, consumer); while (true) { QueueingConsumer.Delivery delivery = consumer.nextDelivery(); String message = new String(delivery.getBody()); String routingKey = delivery.getEnvelope().getRoutingKey(); System.out.println(" [x] Received '" + routingKey + "':'" + message + "'"); } } }
运行接下来的例子,在windows环境中,使用%CP%
,包含指南一种的类路径。 接收全部日志:
$ java -cp $CP ReceiveLogsTopic "#"
接收全部灵巧的kern
日志:
$ java -cp $CP ReceiveLogsTopic "kern.*"
或者你仅仅想接收'critical'日志:
$ java -cp $CP ReceiveLogsTopic "*.critical"
你能够建立多个绑定:
$ java -cp $CP ReceiveLogsTopic "kern.*" "*.critical"
发出一个路由关键字为"kern.critical"的日志,输入:
$ java -cp $CP EmitLogTopic "kern.critical" "A critical kernel error"
跟这些程序玩的开心。注意代码没有对特定的路由和绑定关键字作臆断,你能够操做多于两个的路由关键字参数。
一些难题:
>""绑定会捕获路由关键字是空的消息吗? >"#."会捕获消息中关键字带".."的吗?它会捕获一个单词的关键字吗? >"a.*.#和"a.#"之间有什么不一样?
EmitLogTopic.java 和 ReceiveLogsTopic.java的源代码。
接下来,让咱们在指南的第六部分,弄清当一个远端程序被调用,如何作一个一个往返的消息。