在routing路由模型中,我们实现了可以根据routingKey来选择性地将消息发送到对应的消息队列中,但是,这种模型不够灵活,比如最开始只有warn、info、error、三种类型的日志,但后面如果需要增加fetal类型的日志,则需要修改原有代码,不符合开闭原则。使用topic模型可以比较好的满足这一点,支持通配符的方式来进行扩展。
依旧,我们在官方文档中可以看到详细的说明,使用*
号可以代替一个单词,使用#
号可以代替零个或多个单词。所以,当我想适配所有类型的routingKey时,使用#
号作为routingKey即可。另外,rabbitmq官方还推荐我们使用.
来连接多个单词作为routingKey。
老规矩,我们这次需要使用topic类型的交换机,可以使用已有的,也可以自己重新声明一个新的。
- 生产者
public class Provider {
public void send() throws IOException, TimeoutException {
Connection connection = null;
Channel channel = null;
try {
connection = ConnectionUtils.getConnection();
// 获取连接通道
channel = connection.createChannel();
// 定义通道对应的交换机 参数一:交换机名称 参数二:类型 direct
channel.exchangeDeclare("amqp.topic","topic");
String routingKey = "log.info";
// 发送消息
channel.basicPublish("amqp.topic",routingKey,null,("topic message,routingKey为:" + routingKey + "," + System.currentTimeMillis()).getBytes());
}finally {
if (channel !=null && channel.isOpen()) {
channel.close();
}
if (connection != null && connection.isOpen()) {
connection.close();
}
}
}
public static void main(String[] args) throws IOException, TimeoutException {
Provider provider = new Provider();
provider.send();
}
}
- 消费者
消费者一
public class Consumer01 {
public void consume() throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
// 获取连接通道
final Channel channel = connection.createChannel();
// 绑定交换机
channel.exchangeDeclare("amqp.topic","topic");
//创建临时队列
String queueName = channel.queueDeclare().getQueue();
// 绑定交换机和队列
channel.queueBind(queueName,"amqp.topic","#");
// 每次只能消费一个消息
channel.basicQos(1);
// 消费消息
channel.basicConsume(queueName, false, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费消息:" + new String(body));
//参数一:确认队列中的那个消息 参数二:是否开启多个消息同时确认
channel.basicAck(envelope.getDeliveryTag(),false);
}
});
}
public static void main(String[] args) throws IOException, TimeoutException {
Consumer01 consumer = new Consumer01();
consumer.consume();
}
}
在消费者一中我们即使用了#
号作为routingKey,来接收各种类型的消息。
消费者二:
public class Consumer02 {
public void consume() throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
// 获取连接通道
final Channel channel = connection.createChannel();
// 绑定交换机
channel.exchangeDeclare("amqp.topic","topic");
//创建临时队列
String queueName = channel.queueDeclare().getQueue();
// 绑定交换机和队列
channel.queueBind(queueName,"amqp.topic","*.error");
// 每次只能消费一个消息
channel.basicQos(1);
// 消费消息
channel.basicConsume(queueName, false, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费消息:" + new String(body));
//参数一:确认队列中的那个消息 参数二:是否开启多个消息同时确认
channel.basicAck(envelope.getDeliveryTag(),false);
}
});
}
public static void main(String[] args) throws IOException, TimeoutException {
Consumer02 consumer = new Consumer02();
consumer.consume();
}
}
消费者二中使用*
号代替一个单词,接收以.error
结尾的routingKey类型的消息。
- 测试
启动消费者一和消费者二,再运行生产者,发送一条routingKey为log.info的消息
消费者一:
消费者二:
随即,我们再发送一条routingKey为file.error的消息
此时,消费者一:
消费者二: