消息发送到topic exchange(主题交换器)不能具有任意的routing_key(路由键),它必须是一系列的单词,使用点号.分隔。这些单词可以是任意的,但是经常定义了与消息相关的特性。一些有效的路由键示例:"stock.usd.nyse", "nyse.vmw", "quick.orange.rabbit"。路由键中的字可以任意多个,最多255个字节。
绑定的键必须是相同的形式。主题交换器(topic exchange)背后的逻辑和直接交换器(direct exchange)的逻辑是相似的,一个带特别路由键的消息会被发送到与之路由键匹配的队列。对于路由键有两种重要的特殊情况:
1、*代替一个单词
2、#代替零个或多个单词
使用以下例子能很容易地解释以上情况
在这个例子里,我们会给所有描述的动物发送消息。这些消息使用三个单词(两个点号)组成的路由键进行发送。第一个单词描述速度,第二个单词描述颜色,第三个代表种类:
"
我们创建三个绑定,Q1绑定”*.orange.*”,Q2绑定”*.*.rabbit”和”lazy.#”。
这些绑定可以通过以下两点概况:
1、 Q1对橘黄色的动物感兴趣
2、 Q2想要倾听任何关于rabbit,任何关于lazy 动物的东西。
路由键设置为”quick.orange.rabbit”的消息将被发送到两个队列。消息”lazy.orange.elephant”也将被发送到两个队列。另一方面,”quick.orange.fox”将被发送到Q1队列,“lazy.brown.fox”只会被发送到Q2对列。”lazy.pink.rabbit”将只会被发送到Q2队列一次,虽然它匹配两个绑定。”quick.brown.fox”不能匹配任何绑定,将会被丢弃。
如果我们破坏规则,发送1个或4个单词的消息会发送什么,比如”orange”或者”quick.orange.male.rabbit”?很好,这些消息不会匹配任何的绑定,将会丢失。
另一方面,”lazy.orange.male.rabbit”,虽然有四个单词,但是它匹配了最后的绑定(lazy.#),会被发送到第二个队列。
Topic exchange功能强大,可以做其他交换器做的事情。
当一个对列使用#作为绑定键,它就可以接受任何消息,无视路由键,就像fanout exchange(扇形交换器)。
当对列在绑定键中不使用*或者#,那么topic exchange(主题交换器)就像一个direct exchange(直接交换器)。
实例,以下消费者实例不是同时启动,是分别运行,每次只运行一个消费者实例
生产者
发送消息:
Send quick.orange.rabbit:Hello,RabbitMq1
Send lazy.orange.elephant:Hello,RabbitMq2
Send quick.orange.fox:Hello,RabbitMq3
Send lazy.brown.fox:Hello,RabbitMq4
Send lazy.pink.rabbit:Hello,RabbitMq5
Send quick.brown.fox:Hello,RabbitMq6
package com.enjoy.testrabbitmq;
import com.enjoy.common.RabbitmqConnection;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class TestProducerTopic {
public final static String EXCHANGE_NAME = "topic_logs";
public static void main(String[] args)
throws IOException, TimeoutException {
/* 创建连接,连接到RabbitMQ*/
Connection connection = RabbitmqConnection.getConnection();
/*创建信道*/
Channel channel = connection.createChannel();
/*创建topic交换器*/
channel.exchangeDeclare(EXCHANGE_NAME,"topic");
/*日志消息级别,作为路由键使用*/
String[] routekeys = {
"quick.orange.rabbit"//匹配*.orange.*
,"lazy.orange.elephant"//匹配lazy.#,
,"quick.orange.fox"
,"lazy.brown.fox"
,"lazy.pink.rabbit"
,"quick.brown.fox"};
for(int i=0;i
消费者A
接受到消息:
Received[quick.orange.rabbit]Hello,RabbitMq1
Received[lazy.orange.elephant]Hello,RabbitMq2
Received[quick.orange.fox]Hello,RabbitMq3
package com.enjoy.testrabbitmq;
import com.enjoy.common.RabbitmqConnection;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
*类说明:普通的消费者
*/
public class TestConsumerTopicA {
public static void main(String[] argv)
throws IOException, TimeoutException {
// 打开连接和创建频道,与发送端一样
Connection connection = RabbitmqConnection.getConnection();
final Channel channel = connection.createChannel();
channel.exchangeDeclare(TestProducerTopic.EXCHANGE_NAME,
"topic");
/*声明一个队列*/
String queueName = "orange";
channel.queueDeclare(queueName,false,false,
false,null);
/*绑定,将队列和交换器通过路由键进行绑定*/
String routekey = "*.orange.*";
channel.queueBind(queueName, TestProducerTopic.EXCHANGE_NAME,routekey);
System.out.println("waiting for message........");
/*声明了一个消费者*/
final 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("Received["+envelope.getRoutingKey()
+"]"+message);
}
};
/*消费者正式开始在指定队列上消费消息*/
channel.basicConsume(queueName,true,consumer);
}
}
消费者B
接收消息:
Received[quick.orange.rabbit]Hello,RabbitMq1
Received[lazy.pink.rabbit]Hello,RabbitMq5
package com.enjoy.testrabbitmq;
import com.enjoy.common.RabbitmqConnection;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
*类说明:普通的消费者
*/
public class TestConsumerTopicB {
public static void main(String[] argv)
throws IOException, TimeoutException {
// 打开连接和创建频道,与发送端一样
Connection connection = RabbitmqConnection.getConnection();
final Channel channel = connection.createChannel();
channel.exchangeDeclare(TestProducerTopic.EXCHANGE_NAME,
"topic");
/*声明一个队列*/
String queueName = "rabbit";
channel.queueDeclare(queueName,false,false,
false,null);
/*绑定,将队列和交换器通过路由键进行绑定*/
String bindKey = "*.*.rabbit";
channel.queueBind(queueName, TestProducerTopic.EXCHANGE_NAME,bindKey);
System.out.println("waiting for message........");
/*声明了一个消费者*/
final 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("Received["+envelope.getRoutingKey()
+"]"+message);
}
};
/*消费者正式开始在指定队列上消费消息*/
channel.basicConsume(queueName,true,consumer);
}
}
消费者C
Received[lazy.orange.elephant]Hello,RabbitMq2
Received[lazy.brown.fox]Hello,RabbitMq4
Received[lazy.pink.rabbit]Hello,RabbitMq5
package com.enjoy.testrabbitmq;
import com.enjoy.common.RabbitmqConnection;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
*类说明:普通的消费者
*/
public class TestConsumerTopicC {
public static void main(String[] argv)
throws IOException, TimeoutException {
// 打开连接和创建频道,与发送端一样
Connection connection = RabbitmqConnection.getConnection();
final Channel channel = connection.createChannel();
channel.exchangeDeclare(TestProducerTopic.EXCHANGE_NAME,
"topic");
/*声明一个队列*/
String queueName = "lazy";
channel.queueDeclare(queueName,false,false,
false,null);
/*绑定,将队列和交换器通过路由键进行绑定*/
String bindKey = "lazy.#";
channel.queueBind(queueName, TestProducerTopic.EXCHANGE_NAME,bindKey);
System.out.println("waiting for message........");
/*声明了一个消费者*/
final 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("Received["+envelope.getRoutingKey()
+"]"+message);
}
};
/*消费者正式开始在指定队列上消费消息*/
channel.basicConsume(queueName,true,consumer);
}
}