在上一章咱们改进了咱们的日志系统,若是使用fanout咱们只能简单进行广播,而使用direct则容许消费者能够进行必定程度的选择。可是direct仍是有其局限性,其路由不支持多个条件。java
在咱们的日志系统中,消费者程序可能不止是基于日志的severity,同时也想基于发送日志的源系统。你可能知道linux的syslog工具,它就是同时基于severity(info/warn/crit...)和功能(auth/cron/kern...).linux
这就提供了很大的灵活性-咱们想接收来自cron的严重错误日志和kern的全部日志。bash
下面咱们就使用更复杂的topic来改进咱们的日志系统。工具
发送到topic类型exchange的message不能够具备模糊的routing_key,它必须具备以冒号分割的词。就像"stock.usd.nyse","nyse.vmw","quick.orange.rabbit"等,限制长度255字节。ui
binding key也采用类似的形势。topic exchange的逻辑和direct类似,经过比较message的routing key和bind的binding key,来匹配转发的queue。可是topic的binding支持通配符:spa
经过上面图示的场景来解释会比较好理解。日志
例子中咱们将发送描述动物的message。message会携带routing key(包含三个词),第一个词表示speed,第二个表示color,第三个表示species"<speed>.<colour>.<species>".code
建立了三个绑定:Q1的binding key是”*.orange.*" Q2的binding key是“*.*.rabbit”和 "lazy.#".rabbitmq
以文字表述即是:图片
routing key为“quick.orange.rabbit"的message会同时发布到这两个queue。
routing key为"lazy.orange.elephant"的message会同时发布到这两个queue。
routing key为”quick.orange.fox“只会发布到第一个queue.
routing key为”lazy.brown.fox"的message只会发布到第二个queue.
routing key为"lazy.pink.rabbit"的message虽然知足Q2的两个条件,但也只会发布到Q2一次。
routing key为"quick.brown.fox"的message没有任何匹配,就会被丢失。
若是咱们发送的message只有一个word或者多余三个word,如"orange"或者"quick.orange.male.rabbit"会发生什么呢?这些message不会匹配任何binding key,均会被丢弃掉。
另外"lazy.orange.male.rabbit"虽然具备四个词,可是会匹配最后的binding key,而被发送到第二个queue。
Topic exhange很是强大,同时能够模仿其余两种类型的exchange。当binding key为 # 时,queue会接收全部的message。当binding key中没有使用通配符(* 和 #)时,topic的行为和direct一致。
咱们将在日志系统中使用topic exchange。咱们的routding key采用两个词 "<facility>.<severity>".
EmitLogTopic.java的代码以下:
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) throws Exception { ConnectionFactory factory = new ConnectionFactory(); factory.setHost("localhost"); try (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("UTF-8")); System.out.println(" [x] Sent '" + routingKey + "':'" + message + "'"); } } //.. }
ReceiveLogsTopic.java的代码以下:
import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; import com.rabbitmq.client.DeliverCallback; 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"); DeliverCallback deliverCallback = (consumerTag, delivery) -> { String message = new String(delivery.getBody(), "UTF-8"); System.out.println(" [x] Received '" + delivery.getEnvelope().getRoutingKey() + "':'" + message + "'"); }; channel.basicConsume(queueName, true, deliverCallback, consumerTag -> { }); } }
编译
javac -cp $CP ReceiveLogsTopic.java EmitLogTopic.java
接收全部日志
java -cp $CP ReceiveLogsTopic "#"
接收功能"kern"的日志
java -cp $CP ReceiveLogsTopic "kern.*"
接收严重级别日志
java -cp $CP ReceiveLogsTopic "*.critical"
接收者使用两个绑定条件
java -cp $CP ReceiveLogsTopic "kern.*" "*.critical"
发送日志message
java -cp $CP EmitLogTopic "kern.critical" "A critical kernal error"