一,RabbitMQ的五种模式
1.简单模式
包含一个生产者、一个消费者和一个队列,生产者向队列发送消息,消费者从队列中获取并消费消息。
P为生产者,C为消费者,中间为消息队列,下面我们简单的用代码实现。
首先,新建一个maven项目,导入相关的maven依赖。
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.9.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
简单模式的系统结构,新建一个生产者和消费者。
生产者的代码如下
public class Produce {
@Test
public void sendMessage() throws Exception {
//1.创建一个连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
//2.设置相关参数
connectionFactory.setHost("localhost");
connectionFactory.setPort(5672);
connectionFactory.setUsername("test");
connectionFactory.setPassword("123456");
connectionFactory.setVirtualHost("v-test");
//3.从连接工厂里面创建一个连接
Connection connection = connectionFactory.newConnection();
//4.创建一个通道
Channel channel = connection.createChannel();
/**
* 5.绑定队列
*
* 参数1:队列名,如果发送消息时,队列在mq里面不存在,它会创建一个新的队列;
* 参数2:是否持久化 如果为False,当MQ重启时,消息会丢失;D代表持久化(队列持久化)
* 参数3:是否独享,true代表只有当前的connection可以访问到这个队列;
* 参数4:是否自动删除,是否用完之后就删除这个队列
* 参数5:其他参数
*/
channel.queueDeclare("hello", true, false, false, null);
/**
* 6.发消息
*
* 参数1:交换机
* 参数2:队列名
* 参数3:相关属性(消息持久化)
* 参数4:消息体
*
*/
channel.basicPublish("", "hello", MessageProperties.PERSISTENT_TEXT_PLAIN, "hello rabbitmq".getBytes());
//7.关闭通道和连接
channel.close();
connection.close();
System.out.println("消息发送成功");
}
}
消费者的代码如下
public class Consumer {
@Test
public void receiveMessage() throws Exception {
//1.创建一个连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
//2.设置相关参数
connectionFactory.setHost("localhost");
connectionFactory.setPort(5672);
connectionFactory.setUsername("test");
connectionFactory.setPassword("123456");
connectionFactory.setVirtualHost("v-test");
//3.从连接工厂里面创建一个连接
Connection connection = connectionFactory.newConnection();
//4.创建一个通道
Channel channel = connection.createChannel();
/**
* 5.绑定队列
*
* 参数1:队列名,如果发送消息时,队列在mq里面不存在,它会创建一个新的队列;
* 参数2:是否持久化 如果为False,当MQ重启时,消息会丢失;D代表持久化(队列持久化)
* 参数3:是否独享,true代表只有当前的connection可以访问到这个队列;
* 参数4:是否自动删除,是否用完之后就删除这个队列
* 参数5:其他参数
*/
channel.queueDeclare("hello", true, false, false, null);
/**
* 6.接收消息
*
* 参数一:队列名
* 参数二:是否自动签收
*
*/
channel.basicConsume("hello", true, new DefaultConsumer(channel) {
/**
*
* @param consumerTag
* @param envelope
* @param properties
* @param body 消息体
* @throws IOException
*/
@Override
public void handleDelivery (String consumerTag, Envelope envelope, AMQP.BasicProperties properties,byte[] body) throws
IOException {
System.out.println("消费者接收到消息体:" + new String(body));
}
});
//不能让程序结束
System.in.read();
}
}
生产者发送消息成功
消费者接收消息成功
2.工作模式(workPattern)
工作模式是指多个相互竞争的消费者发送消息的模式,它包含一个生产者、两个消费者和一个队列。两个消费者同时绑定到一个队列上去,当消费者获取消息处理耗时任务时,空闲的消费者从队列中获取b并消费队列。
由于我们的生产者和消费者都要连接Rabbitmq导致有很多重复性代码,所以,将连接代码写成工具类以供调用。
工具类代码
public class RabbitMQUtil {
private static ConnectionFactory connectionFactory;
static {
//创建一个连接工厂
connectionFactory = new ConnectionFactory();
//设置相关参数
connectionFactory.setHost("localhost");
connectionFactory.setPort(5672);
connectionFactory.setUsername("test");
connectionFactory.setPassword("123456");
connectionFactory.setVirtualHost("/v-test");
}
/**
* 获取连接的方法
*/
public static Connection getConnection() {
try {
//从连接工厂里面创建一个连接
Connection connection = connectionFactory.newConnection();
return connection;
} catch (Exception e) {
System.out.println("抛出异常:" + e);
}
return null;
}
/**
* 关闭连接及通道
* @param connection
* @param channel
*/
public static void closeChannelAndConnection(Connection connection, Channel channel){
try{
connection.close();
channel.close();
}catch (Exception e){
System.out.println(e);
}
}
}
工作模式的代码结构
生产者的代码:
public class Produce {
@Test
public void sendMessage() throws Exception {
Connection connection = RabbitMQUtil.getConnection();
//创建信道
Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare("hello", false, false, false, null);
//发送消息
for (int i = 0; i < 10; i++) {
channel.basicPublish("", "hello", null, ("helloRabbitMq" + i).getBytes());
}
//关闭
RabbitMQUtil.closeChannelAndConnection(connection, channel);
System.out.println("消息发送完成");
}
}
消费者1的代码:
public class Consumer1 {
private int i = 0;
@Test
public void receiveMessage() throws Exception {
Connection connection = RabbitMQUtil.getConnection();
//创建信道
final Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare("hello", false, false, false, null);
//设置一次只接收一条未接受的数据
channel.basicQos(2);
//接收消息
channel.basicConsume("hello", false, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
// try {
// Thread.sleep(500);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// i++;
// if (i <= 2) {
// int a = 10 / 0;
// }
System.out.println("消费者【1】接收到消息:" + new String(body));
/**
* 手动确认签收消息(消费完一条消息要自动发送消息给MQ)
* 标签1:消息的投递ID
* 标签2:是否批量签收
*/
channel.basicAck(envelope.getDeliveryTag(), false);
}
});
//不能让程序结束
System.in.read();
RabbitMQUtil.closeChannelAndConnection(connection, channel);
}
}
消费者2的代码:
public class Consemer2 {
@Test
public void receiveMessage() throws Exception {
Connection connection = RabbitMQUtil.getConnection();
//创建信道
Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare("hello", false, false, false, null);
//接收消息
channel.basicConsume("hello", true, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者【2】接收到消息:" + new String(body));
}
});
//不能让程序结束
System.in.read();
RabbitMQUtil.closeChannelAndConnection(connection, channel);
}
}
运行结果:
根据两个消费者的消费力不同,所以消费的数据量并不相同。
3.发布/订阅模式(fanout)
指同时向多个消费者发送消息的模式(类似广播),它包括一个生产者、两个消费者、两个队列和一个交换机。两个消费者同时绑定到不同的队列上,两个队列绑定到交换机上去,生产者通过发送消息到交换机,所有消费者接收并消费消息。
fanout模式结构
生产者代码:
public class Produce {
@Test
public void sendMessage() throws Exception{
Connection connection = RabbitMQUtil.getConnection();
//创建信道
Channel channel = connection.createChannel();
//设置交换机
channel.exchangeDeclare("logs", BuiltinExchangeType.FANOUT);
//向交换机发消息
channel.basicPublish("logs","",null,"我是一个fount类型的消息".getBytes());
//关闭资源
RabbitMQUtil.closeChannelAndConnection(connection,channel);
}
}
消费者1代码:
public class Consumer1 {
@Test
public void receiveMessage() throws Exception {
Connection connection = RabbitMQUtil.getConnection();
//创建信道
Channel channel = connection.createChannel();
//设置交换机
channel.exchangeDeclare("logs", BuiltinExchangeType.FANOUT);
//从通道里面获取一个临时队列
String queue = channel.queueDeclare().getQueue();
/**
* 把临时队列和交换机进行绑定
*
* 参数1:队列
* 参数2:交换机
* 参数3:路由key
*/
channel.queueBind(queue,"logs","");
//接收消息
channel.basicConsume(queue,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者【1】接收到消息"+new String(body));
}
});
System.out.println("消费者【1】启动成功");
//不能让程序结束
System.in.read();
}
}
消费者2代码:
public class Consumer2 {
@Test
public void receiveMessage() throws Exception {
Connection connection = RabbitMQUtil.getConnection();
//创建信道
Channel channel = connection.createChannel();
//设置交换机
channel.exchangeDeclare("logs", BuiltinExchangeType.FANOUT);
//从通道里面获取一个临时队列
String queue = channel.queueDeclare().getQueue();
/**
* 把临时队列和交换机进行绑定
*
* 参数1:队列
* 参数2:交换机
* 参数3:路由key
*/
channel.queueBind(queue,"logs","");
//接收消息
channel.basicConsume(queue,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者【2】接收到消息"+new String(body));
}
});
System.out.println("消费者【2】启动成功");
//不能让程序结束
System.in.read();
}
}
结果:
4.路由模式
路由模式是可以根据路由键选择性给多个消费者发送消息的模式,它包括一个生产者、两个消费者、两个队列和一个交换机。两个消费者同时绑定到不同的不同的队列上去,两个队列通过路由键绑定到交换机上,生产者发送消息到交换机,交换机通过路由键转发到不同队列,队列绑定的消费者接受消息。
代码结构:
生产者代码:
public class Produce {
@Test
public void sendMessage() throws Exception{
Connection connection = RabbitMQUtil.getConnection();
//创建信道
Channel channel = connection.createChannel();
//设置交换机
channel.exchangeDeclare("logs", BuiltinExchangeType.DIRECT);
//向交换机发消息 打了四条消息,分别指定了路由key
channel.basicPublish("logs","info",null,"我是一个routingKey-direct类型的消息-info".getBytes());
channel.basicPublish("logs","warm",null,"我是一个routingKey-direct类型的消息-warm".getBytes());
channel.basicPublish("logs","debug",null,"我是一个routingKey-direct类型的消息-debug".getBytes());
channel.basicPublish("logs","error",null,"我是一个routingKey-direct类型的消息-error".getBytes());
//关闭资源
RabbitMQUtil.closeChannelAndConnection(connection,channel);
}
}
消费者代码:
public class Consumer1 {
@Test
public void receiveMessage() throws Exception {
Connection connection = RabbitMQUtil.getConnection();
//创建信道
Channel channel = connection.createChannel();
//设置交换机
channel.exchangeDeclare("logs", BuiltinExchangeType.DIRECT);
//从通道里面获取一个临时队列
String queue = channel.queueDeclare().getQueue();
/**
* 把临时队列和交换机进行绑定
*
* 参数1:队列
* 参数2:交换机
* 参数3:路由key
*/
channel.queueBind(queue,"logs","info");
channel.queueBind(queue,"logs","warm");
//接收消息
channel.basicConsume(queue,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者【info】接收到消息"+new String(body));
}
});
System.out.println("消费者【info】启动成功");
//不能让程序结束
System.in.read();
}
}
结果:
消费者值监听了绑定路由key的消息,其他消息并未被消费。
5.通配符模式
通配符模式是可以根据路由键匹配规则选择性给多个消费者发送消息的模式,它包含一个生产者、两个消费者、两个队列和一个交换机。两个消费者同时绑定到不同的队列上去,两个队列通过路由键匹配规则绑定到交换机上去,生产者发送消息到交换机,交换机通过路由键匹配规则转发到不同队列,队列绑定的消费者接收并消费消息。
代码结构
生产者代码
public class Produce {
@Test
public void sendMessage() throws Exception{
Connection connection = RabbitMQUtil.getConnection();
//创建信道
Channel channel = connection.createChannel();
//设置交换机
channel.exchangeDeclare("topic", BuiltinExchangeType.TOPIC);
//向交换机发消息 打了四条消息,分别指定了路由key
channel.basicPublish("topic","com.mean",null,"我是一个routingKey-topic类型的消息mean".getBytes());
channel.basicPublish("topic","com.mean.src",null,"我是一个routingKey-topic类型的消息mean.src".getBytes());
//关闭资源
RabbitMQUtil.closeChannelAndConnection(connection,channel);
}
}
消费者*的代码:
public class Consumer1 {
@Test
public void receiveMessage() throws Exception {
Connection connection = RabbitMQUtil.getConnection();
//创建信道
Channel channel = connection.createChannel();
//设置交换机
channel.exchangeDeclare("topic", BuiltinExchangeType.TOPIC);
//从通道里面获取一个临时队列
String queue = channel.queueDeclare().getQueue();
/**
* 把临时队列和交换机进行绑定
*
* 参数1:队列
* 参数2:交换机
* 参数3:路由key
*/
channel.queueBind(queue,"topic","com.*");
//接收消息
channel.basicConsume(queue,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者【*】接收到消息"+new String(body));
}
});
System.out.println("消费者【*】启动成功");
//不能让程序结束
System.in.read();
}
}
消费者#的代码:
public class Consumer2 {
@Test
public void receiveMessage() throws Exception {
Connection connection = RabbitMQUtil.getConnection();
//创建信道
Channel channel = connection.createChannel();
//设置交换机
channel.exchangeDeclare("topic", BuiltinExchangeType.TOPIC);
//从通道里面获取一个临时队列
String queue = channel.queueDeclare().getQueue();
/**
* 把临时队列和交换机进行绑定
*
* 参数1:队列
* 参数2:交换机
* 参数3:路由key
*/
channel.queueBind(queue,"topic","com.#");
//接收消息
channel.basicConsume(queue,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者【#】接收到消息"+new String(body));
}
});
System.out.println("消费者【#】启动成功");
//不能让程序结束
System.in.read();
}
}
运行结果:
由此可直观的看到#和 * 的区别,即符号“#”匹配路由键的一个或多个词,“*”符号匹配路由键的一个词。