主题转发器
发送到主题转发器的消息不能有任意的 routing_key - 它必须是由点分隔的单词列表。这些单词可以是任何东西,但通常它们指定与消息相关联的一些功能。几个有效的routeKey示例:“ stock.usd.nyse”,“ nyse.vmw ”,“ quick.orange.rabbit ”。routeKey中可以有任意多的单词,最多可达255个字节。
绑定键(即绑定时使用的routeKey)也必须是相同的形式。topics转发器可以是用通配符:
- *(星)可以替代一个单词。
- #(哈希)可以替换零个或多个单词。
如下图:
在这个例子中,我们将发送所有描述动物的消息。消息将使用由三个字(两个点)组成的routeKey发送。routeKey中的第一个单词将描述速度,第二个描述颜色,第三个描述种类( <speed>.<color>.<species>)。
我们创建了三个绑定:Q1队列绑定键* .orange.*,Q2绑定键*.*.rabbit和lazy.#。
这些绑定可以总结为:
- Q1对所有的橙色动物感兴趣。
- Q2想听听有关兔子的一切,以及关于懒惰动物的一切。
将routeKey设置为“ 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 ”即使它有四个单词,却会匹配最后的绑定,并将消息传递到第二个队列。
主题转发器
主题转发是强大的,其他转发器能做到的事情,它也能做到。
当队列与“ # ”(哈希)绑定键绑定时,它将接收所有消息,而不管绑定键是什么,就像fanout转发器一样。
当特殊字符“ * ”(星号)和“ # ”(哈希)在绑定中不被使用时,主题转发器将表现得像一个直接转发器。
完整示例
EmitLogTopic.java的代码:
1 package com.rabbitMQ; 2 3 import com.rabbitmq.client.Channel; 4 import com.rabbitmq.client.Connection; 5 import com.rabbitmq.client.ConnectionFactory; 6 7 public class EmitLogTopic { 8 9 private static final String EXCHANGE_NAME = "topic_logs"; 10 11 public static void main(String[] argv) 12 throws Exception { 13 14 ConnectionFactory factory = new ConnectionFactory(); 15 factory.setHost("localhost"); 16 Connection connection = factory.newConnection(); 17 Channel channel = connection.createChannel(); 18 19 channel.exchangeDeclare(EXCHANGE_NAME, "topic"); 20 21 String routingKey = getRouting(argv); 22 String message = getMessage(argv); 23 24 channel.basicPublish(EXCHANGE_NAME, routingKey, null, message.getBytes()); 25 System.out.println(" [x] Sent '" + routingKey + "':'" + message + "'"); 26 27 connection.close(); 28 } 29 30 31 private static String getRouting(String[] argv) { 32 33 if (argv.length == 0) { 34 35 return "jr;;p.critical"; 36 } 37 String str = argv[0]; 38 39 String routeKey = str.replaceAll("=.*", ""); 40 41 return routeKey; 42 } 43 44 private static String getMessage(String[] argv) { 45 if (argv.length == 0) { 46 47 return "I like play game."; 48 } 49 String str = argv[0]; 50 String message = str.replaceAll(".*=", ""); 51 return message; 52 } 53 54 55 }
ReceiveLogsTopic.java的代码:
1 package com.rabbitMQ; 2 3 import java.io.IOException; 4 5 import com.rabbitmq.client.AMQP; 6 import com.rabbitmq.client.Channel; 7 import com.rabbitmq.client.Connection; 8 import com.rabbitmq.client.ConnectionFactory; 9 import com.rabbitmq.client.Consumer; 10 import com.rabbitmq.client.DefaultConsumer; 11 import com.rabbitmq.client.Envelope; 12 13 public class ReceiveLogsTopic { 14 private static final String EXCHANGE_NAME = "topic_logs"; 15 16 public static void main(String[] argv) throws Exception { 17 ConnectionFactory factory = new ConnectionFactory(); 18 factory.setHost("localhost"); 19 Connection connection = factory.newConnection(); 20 Channel channel = connection.createChannel(); 21 22 channel.exchangeDeclare(EXCHANGE_NAME, "topic"); 23 String queueName = channel.queueDeclare().getQueue(); 24 25 if (argv.length < 1) { 26 System.err.println("Usage: ReceiveLogsTopic [binding_key]..."); 27 System.exit(1); 28 } 29 30 for (String bindingKey : argv) { 31 channel.queueBind(queueName, EXCHANGE_NAME, bindingKey); 32 } 33 34 System.out.println(" [*] Waiting for messages. To exit press CTRL+C"); 35 36 Consumer consumer = new DefaultConsumer(channel) { 37 @Override 38 public void handleDelivery(String consumerTag, Envelope envelope, 39 AMQP.BasicProperties properties, byte[] body) throws IOException { 40 String message = new String(body, "UTF-8"); 41 System.out.println(" [x] Received '" + envelope.getRoutingKey() + "':'" + message + "'"); 42 System.out.println(consumerTag);//amq.ctag-yqB24qMf7hEtpyZgR1p4MQ 43 System.out.println(properties);//#contentHeader<basic>(content-type=null, content-encoding=null, headers=null, delivery-mode=null, priority=null, correlation-id=null, reply-to=null, expiration=null, message-id=null, timestamp=null, type=null, user-id=null, app-id=null, cluster-id=null) 44 System.out.println(envelope);//Envelope(deliveryTag=1传送标志, redeliver=false重新传送, exchange=topic_logs转发器名称, routingKey=jr;;p.critical) 45 } 46 }; 47 channel.basicConsume(queueName, true, consumer); 48 } 49 }
启动ReceiveLogsTopic.java,给它传*.error
启动ReceiveLogsTopic.java,给它传*.info
启动EmitLogTopic.java,给它传#=all matches
结果:俩个ReceiveLogsTopic.java会收到all matches
启动ReceiveLogsTopic.java,给它传mistake=error
只有第一个ReceiveLogsTopic.java收到消息