RabbitMQ消息传递模型中的核心思想是生产者从不将任何消息直接发送到队列。生产者经常甚至根本不知道是否将消息传递到任何队列。
生产者只能将消息发送到交换机。交换机,一方面,它接收来自生产者的消息,另一方面,将它们推入队列。
交换机必须知道如何处理收到的消息,是否应将其附加到特定队列?是否应该将其附加到许多队列中?还是应该丢弃它。规则由交换类型定义 。
在前面部分中,我们对交换一无所知,但仍然能够将消息发送到队列。这是可能的,因为我们使用的是默认交换,我们通过空字符串(“”)进行标识:
channel.basicPublish(“”,“ routingKey”,null,message.getBytes());
第一个参数是交换的名称。空字符串表示默认或无名称交换:消息将以routingKey指定的名称路由到队列(如果存在)
消息的流程如下:
// 声明交换机
// 参数一:交换机的名称,rabbitMq如果不存在,会自动创建
// 参数二:交换机的类型,这里就是fanout
channel.exchangeDeclare("fanout_demo","fanout");
// 发送消息,此时就是把消息投递给了交换机
// 参数一:交换机的名称
// 参数二:路由key,在广播模式下,路由key是没有意义的,因为广播,要把消息投递给所有的队列,才能实现广播不是
// 参数三:传递消息的额外设置
// 参数四:消息体
channel.basicPublish("fanout_demo","",null,message.getBytes(Charset.defaultCharset()));
之前两种没有交换机参与的模式,路由key就是队列的名称,因为直接和队列沟通的
而这里需要交换机根据key将消息路由到不同的队列。
无论何时连接到Rabbit,我们都需要一个全新的空队列。为此,我们可以创建一个具有随机名称的队列,或者甚至更好-让服务器为我们选择一个随机队列名称。
其次,一旦我们断开了使用者的连接,队列将被自动删除。
不向queueDeclare()提供任何参数时,将 使用生成的名称创建一个非持久的,排他的,自动删除的队列:
// 8 创建一个临时队列, 返回队列的名字
String queueName = channel.queueDeclare().getQueue();
// 参数一:队列的名字
// 参数二:交换机的名字
// 参数三:路由的key
channel.queueBind(queueName,"fanout_demo","");
03-rabbitmq-fanout
<dependencies>
<dependency>
<groupId>com.rabbitmqgroupId>
<artifactId>amqp-clientartifactId>
<version>5.10.0version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.16version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-apiartifactId>
<version>1.7.30version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>1.2.3version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13.1version>
<scope>testscope>
dependency>
dependencies>
package study.wyy.mq.rabbit.fanout.producer;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.nio.charset.Charset;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeoutException;
/**
* @author by wyaoyao
* @Description: 生产者
* @Date 2020/12/27 9:45 上午
*/
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
// 1 构建连接对象
ConnectionFactory connectionFactory = new ConnectionFactory();
// 2 设置mq的主机地址
connectionFactory.setHost("localhost");
// 3 设置端口号
connectionFactory.setPort(5672);
// 4 设置要连接的虚拟主机
connectionFactory.setVirtualHost("/rabbit_study");
// 5 设置用户名和密码
connectionFactory.setUsername("wyy");
connectionFactory.setPassword("wyy123");
// 6 创建连接对象
Connection connection = connectionFactory.newConnection();
// 7 创建连接中的chanel
Channel channel = connection.createChannel();
// 8 声明交换机
// 参数一:交换机的名称,rabbitMq如果不存在,会自动创建
// 参数二:交换机的类型,这里就是fanout
channel.exchangeDeclare("fanout_demo","fanout");
// 9 发送消息,此时就是把消息投递给了交换机
// 参数一:交换机的名称
// 参数二:路由key,在广播模式下,路由key是没有意义的,因为广播,要把消息投递给所有的队列,才能实现广播不是
// 参数三:传递消息的额外设置
// 参数四:消息体
String format = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss"));
String message = "一条广播消息 " + format;
channel.basicPublish("fanout_demo","",null,message.getBytes(Charset.defaultCharset()));
channel.close();
connection.close();
}
}
package study.wyy.mq.rabbit.fanout.consumer;
import com.rabbitmq.client.*;
import lombok.extern.slf4j.Slf4j;
import sun.jvm.hotspot.debugger.win32.coff.COFFLineNumber;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.concurrent.TimeoutException;
/**
* @author by wyaoyao
* @Description
* @Date 2020/12/27 11:05 上午
*/
@Slf4j
public class Consumer1 {
public static void main(String[] args) throws IOException, TimeoutException {
// 1 链接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
// 2 设置mq的地址
connectionFactory.setHost("localhost");
connectionFactory.setPort(5672);
// 3 设置连接的虚拟主机
connectionFactory.setVirtualHost("/rabbit_study");
// 4 设置用户名和密码
connectionFactory.setUsername("wyy");
connectionFactory.setPassword("wyy123");
// 5 获取连接对象
Connection connection = connectionFactory.newConnection();
// 6 获取channel
Channel channel = connection.createChannel();
// 7 绑定刚刚的交换机
channel.exchangeDeclare("fanout_demo","fanout");
// 8 创建一个临时队列, 返回队列的名字, 当然也可以不使用临时队列,想之前一样生命一个队列
String queueName = channel.queueDeclare().getQueue();
// 9 绑定交换机和队列
// 参数一:队列的名字
// 参数二:交换机的名字
// 参数三:路由的key
channel.queueBind(queueName,"fanout_demo","");
// 消费消息
// 参数一:队列的名字
// 参数二:是否开启自动化确认
// 参数三:回调接口
channel.basicConsume(queueName,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
log.info("消费者1:{}",new String(body, Charset.defaultCharset()));
}
});
}
}
Consumer2:
package study.wyy.mq.rabbit.fanout.consumer;
import com.rabbitmq.client.*;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.concurrent.TimeoutException;
/**
* @author by wyaoyao
* @Description
* @Date 2020/12/27 11:05 上午
*/
@Slf4j
public class Consumer2 {
public static void main(String[] args) throws IOException, TimeoutException {
// 1 链接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
// 2 设置mq的地址
connectionFactory.setHost("localhost");
connectionFactory.setPort(5672);
// 3 设置连接的虚拟主机
connectionFactory.setVirtualHost("/rabbit_study");
// 4 设置用户名和密码
connectionFactory.setUsername("wyy");
connectionFactory.setPassword("wyy123");
// 5 获取连接对象
Connection connection = connectionFactory.newConnection();
// 6 获取channel
Channel channel = connection.createChannel();
// 7 绑定刚刚的交换机
channel.exchangeDeclare("fanout_demo","fanout");
// 8 声明一个队列
// 参数一:队列的名称
// 参数二:是否持久化
// 参数三:是否独占
// 参数四:是否在消费完成后自动删除队列
AMQP.Queue.DeclareOk fanout_queue = channel.queueDeclare("fanout_queue", false, false, true, null);
// 9 绑定交换机和队列
// 参数一:队列的名字
// 参数二:交换机的名字
// 参数三:路由的key
channel.queueBind(fanout_queue.getQueue(),"fanout_demo","");
// 消费消息
// 参数一:队列的名字
// 参数二:是否开启自动化确认
// 参数三:回调接口
channel.basicConsume(fanout_queue.getQueue(),true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
log.info("消费者2:{}",new String(body, Charset.defaultCharset()));
}
});
}
}
11:21:13.060 [pool-1-thread-4] INFO study.wyy.mq.rabbit.fanout.consumer.Consumer1 - 消费者1:一条广播消息 2020-12-27 11:21:13
11:21:13.052 [pool-1-thread-4] INFO study.wyy.mq.rabbit.fanout.consumer.Consumer2 - 消费者2:一条广播消息 2020-12-27 11:21:13
两个消费者都收到了消息
在Fanout模式中,一条消息会被所有订阅的队列都消费。但是,某些情况下,希望不同的消息被不同的队列消费,这时就要用到Direct类型的Exchange(交换机)
在Direct模型下:
上图中的orange,black,green就是不同的路由key
新建一个model:04-rabbitmq-route
<dependencies>
<dependency>
<groupId>com.rabbitmqgroupId>
<artifactId>amqp-clientartifactId>
<version>5.10.0version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.16version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-apiartifactId>
<version>1.7.30version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>1.2.3version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13.1version>
<scope>testscope>
dependency>
dependencies>
package study.wyy.mq.rabbit.route.producer;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.concurrent.TimeoutException;
/**
* @author by wyaoyao
* @Description
* @Date 2020/12/27 2:35 下午
*/
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
// 1 连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
// 2 设置mq地址
connectionFactory.setHost("localhost");
connectionFactory.setPort(5672);
// 3 设置要连接的虚拟主机
connectionFactory.setVirtualHost("/rabbit_study");
// 4 设置账户
connectionFactory.setUsername("wyy");
connectionFactory.setPassword("wyy123");
// 5 获取连接
Connection connection = connectionFactory.newConnection();
// 6 创建连接中的通道
Channel channel = connection.createChannel();
// 7 通过channel声明交换机
// 参数一:交换机名称
// 参数二:交换机类型
String exchangeName = "direct_demo";
channel.exchangeDeclare(exchangeName,"direct");
// 8 发送消息
// 参数一:交换机的名称
// 参数二:路由key:
// 参数三:传递消息的的其他配置,比如是否持久化消息
// 参数四:消息体
// 这里就发三条消息
channel.basicPublish(exchangeName,"orange",null,"current color is orange".getBytes(Charset.defaultCharset()));
channel.basicPublish(exchangeName,"green",null,"current color is green".getBytes(Charset.defaultCharset()));
channel.basicPublish(exchangeName,"black",null,"current color is black".getBytes(Charset.defaultCharset()));
// 关闭资源
channel.close();
connection.close();
}
}
package study.wyy.mq.rabbit.route.consumer;
import com.rabbitmq.client.*;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.concurrent.TimeoutException;
/**
* @author by wyaoyao
* @Description: 消费者,只消费路由key是orange的消息
* @Date 2020/12/27 2:47 下午
*/
@Slf4j
public class Consumer1 {
public static void main(String[] args) throws IOException, TimeoutException {
// 1 连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
// 2 设置mq地址
connectionFactory.setHost("localhost");
connectionFactory.setPort(5672);
// 3 设置要连接的虚拟主机
connectionFactory.setVirtualHost("/rabbit_study");
// 4 设置用户名和密码
connectionFactory.setUsername("wyy");
connectionFactory.setPassword("wyy123");
// 5 获取连接
Connection connection = connectionFactory.newConnection();
// 6 创建通道
Channel channel = connection.createChannel();
String exchangeName = "direct_demo";
// 7 通过通道声明交换机
// 参数一:交换机的名称 参数二:交换机的类型
channel.exchangeDeclare(exchangeName,"direct");
// 8 创建一个临时队列
String queue = channel.queueDeclare().getQueue();
// 9 基于路由key,绑定交换机和队列
channel.queueBind(queue,exchangeName,"orange");
// 10 消费消息
// 参数一:队列的名字
// 参数二:是否自动确认
// 参数三:回调函数
channel.basicConsume(queue,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
log.info("消费者1 -> {}", new String(body, Charset.defaultCharset()));
}
});
}
}
package study.wyy.mq.rabbit.route.consumer;
import com.rabbitmq.client.*;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.concurrent.TimeoutException;
/**
* @author by wyaoyao
* @Description: 消费者,只消费路由key是green和black的消息
* @Date 2020/12/27 2:47 下午
*/
@Slf4j
public class Consumer2 {
public static void main(String[] args) throws IOException, TimeoutException {
// 1 连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
// 2 设置mq地址
connectionFactory.setHost("localhost");
connectionFactory.setPort(5672);
// 3 设置要连接的虚拟主机
connectionFactory.setVirtualHost("/rabbit_study");
// 4 设置用户名和密码
connectionFactory.setUsername("wyy");
connectionFactory.setPassword("wyy123");
// 5 获取连接
Connection connection = connectionFactory.newConnection();
// 6 创建通道
Channel channel = connection.createChannel();
String exchangeName = "direct_demo";
// 7 通过通道声明交换机
// 参数一:交换机的名称 参数二:交换机的类型
channel.exchangeDeclare(exchangeName,"direct");
// 8 创建一个临时队列
String queue = channel.queueDeclare().getQueue();
// 9 基于路由key,绑定交换机和队列,将路由是green和black绑定到这个队列中
channel.queueBind(queue,exchangeName,"green");
channel.queueBind(queue,exchangeName,"black");
// 10 消费消息
// 参数一:队列的名字
// 参数二:是否自动确认
// 参数三:回调函数
channel.basicConsume(queue,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
log.info("消费者2 -> {}", new String(body, Charset.defaultCharset()));
}
});
}
}
15:00:48.345 [pool-1-thread-4] INFO study.wyy.mq.rabbit.route.consumer.Consumer1 - 消费者1 -> current color is orange
15:00:48.345 [pool-1-thread-4] INFO study.wyy.mq.rabbit.route.consumer.Consumer2 - 消费者2 -> current color is green
15:00:48.349 [pool-1-thread-4] INFO study.wyy.mq.rabbit.route.consumer.Consumer2 - 消费者2 -> current color is black
可见消费者1只消费了消费路由orange的消息
消费者2只消费了消费路由key是green和black的消息
可以使用占位符,动态配置路由key,实现动态路由
Topic类型的交换机和Direct类型相比,都是可以根据routeKey把消息路由到不同的消息队列,只不过topic类型的交换机可以让队列绑定routeKey的时候使用通配符。一般是由一个或者多个单词组成的,多个单词之前使用.
分割,例如:user.save
*
可以代替一个单词。#
可以替代零个或多个单词。package study.wyy.mq.rabbit.topic.producer;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.concurrent.TimeoutException;
/**
* @author by wyaoyao
* @Description
* @Date 2020/12/27 3:56 下午
*/
@Slf4j
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
// 1 连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
// 2 设置mq地址
connectionFactory.setHost("localhost");
connectionFactory.setPort(5672);
// 3 设置虚拟主机
connectionFactory.setVirtualHost("/rabbit_study");
// 4 设置用户
connectionFactory.setUsername("wyy");
connectionFactory.setPassword("wyy123");
// 5 获取连接
Connection connection = connectionFactory.newConnection();
// 6 创建通道
Channel channel = connection.createChannel();
// 7 声明交换机
// 参数一:交换机的名字
// 参数二:交换机的类型
channel.exchangeDeclare("topic_demo","topic");
// 8 发送消息
// 参数一交换机的名字
// 参数二: 路由key
// 参数三:额外设置,比如是否持久化数据
// 参数四:消息体
String routeKey1 = "user.*";
channel.basicPublish("topic_demo",routeKey1,null,"user is save".getBytes(Charset.defaultCharset()));
channel.basicPublish("topic_demo",routeKey1,null,"user is update".getBytes(Charset.defaultCharset()));
// 再发两条信息,使用的是routeKey2
String routeKey2 = "item.*";
channel.basicPublish("topic_demo",routeKey2,null,"item is save".getBytes(Charset.defaultCharset()));
channel.basicPublish("topic_demo",routeKey2,null,"item is update".getBytes(Charset.defaultCharset()));
// 9 关闭资源
channel.close();
connection.close();
}
}
package study.wyy.mq.rabbit.topic.consumer;
import com.rabbitmq.client.*;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.concurrent.TimeoutException;
/**
* @author by wyaoyao
* @Description
* @Date 2020/12/27 4:06 下午
*/
@Slf4j
public class ItemConsumer {
public static void main(String[] args) throws IOException, TimeoutException {
// 1 链接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
// 2 设置mq地址
connectionFactory.setHost("localhost");
connectionFactory.setPort(5672);
// 3 设置虚拟主机
connectionFactory.setVirtualHost("/rabbit_study");
// 4 设置用户名和密码
connectionFactory.setUsername("wyy");
connectionFactory.setPassword("wyy123");
// 5 获取连接
Connection connection = connectionFactory.newConnection();
// 6 获取通道
Channel channel = connection.createChannel();
// 7 声明交换机
channel.exchangeDeclare("topic_demo","topic");
// 8 创建队列
String queue = channel.queueDeclare().getQueue();
// 9 绑定队列和交换机
// 参数一:队列名称
// 参数二:交换机名称
// 参数三:路由key
channel.queueBind(queue,"topic_demo","item.*");
// 8 消费消息
// 参数一:队列名称
// 参数二:是否自动确认
// 参数三:回调接口
channel.basicConsume(queue,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
log.info("itemConsumer -> {}",new String(body, Charset.defaultCharset()));
}
});
}
}
package study.wyy.mq.rabbit.topic.consumer;
import com.rabbitmq.client.*;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.concurrent.TimeoutException;
/**
* @author by wyaoyao
* @Description
* @Date 2020/12/27 4:06 下午
*/
@Slf4j
public class UserConsumer {
public static void main(String[] args) throws IOException, TimeoutException {
// 1 链接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
// 2 设置mq地址
connectionFactory.setHost("localhost");
connectionFactory.setPort(5672);
// 3 设置虚拟主机
connectionFactory.setVirtualHost("/rabbit_study");
// 4 设置用户名和密码
connectionFactory.setUsername("wyy");
connectionFactory.setPassword("wyy123");
// 5 获取连接
Connection connection = connectionFactory.newConnection();
// 6 获取通道
Channel channel = connection.createChannel();
// 7 声明交换机
channel.exchangeDeclare("topic_demo","topic");
// 8 创建队列
String queue = channel.queueDeclare().getQueue();
// 9 绑定队列和交换机
// 参数一:队列名称
// 参数二:交换机名称
// 参数三:路由key
channel.queueBind(queue,"topic_demo","user.*");
// 8 消费消息
// 参数一:队列名称
// 参数二:是否自动确认
// 参数三:回调接口
channel.basicConsume(queue,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
log.info("userConsumer -> {}",new String(body, Charset.defaultCharset()));
}
});
}
}
测试结果
16:47:12.250 [pool-1-thread-4] INFO study.wyy.mq.rabbit.topic.consumer.ItemConsumer - itemConsumer -> item is save
16:47:12.265 [pool-1-thread-4] INFO study.wyy.mq.rabbit.topic.consumer.ItemConsumer - itemConsumer -> item is update
16:47:12.259 [pool-1-thread-4] INFO study.wyy.mq.rabbit.topic.consumer.UserConsumer - userConsumer -> user is save
16:47:12.267 [pool-1-thread-5] INFO study.wyy.mq.rabbit.topic.consumer.UserConsumer - userConsumer -> user is update
路由key为user.*
被UserConsumer监听到消费了
路由key为item.*
被ItemConsumer监听到消费了