基本概念:
-
生产者
向队列中发送消息的程序
-
消费者
从队列中消费消息的程序
-
队列 queue
消息的容器,FIFO数据结构
-
交换机 exchange
将消息复制分发到不同的队列中
-
虚拟主机 host
包含交换机和队列的空间
-
连接 connection
客户端和MQ服务的连接
-
通道 channel
通信的通道
基本使用
用户
虚拟主机
队列
交换机
五种消息模型
RabbitMQ提供了多种消息模型,官网上第6种是RPC不属于常规的消息队列。 属于消息模型的是前5种:
-
简单的一对一模型
-
工作队列模型 ,一个生产者将消息分发给多个消费者
-
发布/订阅模型 ,生产者发布消息,多个消费者同时收取
-
路由模型 ,生产者通过关键字发送消息给特定消费者
-
主题模型 ,路由模式基础上,在关键字里加入了通配符
一对一模型
导入依赖
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>3.4.1</version>
</dependency>
MQ工具类
package com.blb.rabbitmq_demo.until;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
public class MQUntil {
/**
* 获得MQ连接
* @return
*/
public static Connection getMQConnection() throws IOException {
ConnectionFactory connectionFactory = new ConnectionFactory();
// 配置服务名、端口、虚拟主机名、登录账号和密码
connectionFactory.setHost("localhost");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("MyGuest");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
return connectionFactory.newConnection();
}
}
生产者
package com.blb.rabbitmq_demo.helloword;
import com.blb.rabbitmq_demo.until.MQUntil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
/**
* 生产者发送简单的消息队列
*/
public class SimpleProduct{
public static void main(String[] args) throws IOException {
// 获得连接
Connection connection = MQUntil.getMQConnection();
// 创建通道
Channel channel = connection.createChannel();
// 声明队列
channel.queueDeclare("MyQueues",false,false,false,null);
// 发送消息 交换机名,队列名,手动确认false,配置属性,数据
channel.basicPublish("","MyQueues",false,null,"hello world".getBytes());
channel.close();
connection.close();
}
}
消费者
package com.blb.rabbitmq_demo.helloword;
import com.blb.rabbitmq_demo.until.MQUntil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;
import java.io.IOException;
public class SimpleConsumer {
public static void main(String[] args) throws IOException, InterruptedException {
// 获得连接
Connection connection = MQUntil.getMQConnection();
// 获得通道
Channel channel = connection.createChannel();
// 在队列服务器上声明队列
channel.queueDeclare("MyQueues",false,false,false,null);
// 创建消费者
QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
// 消费者消息通道中的消息
channel.basicConsume("MyQueues",true,queueingConsumer);
// 读取消息
while (true){
QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery();
System.out.println(new String(delivery.getBody()));
}
}
}
运行结果
工作队列
生产者
package com.blb.rabbitmq_demo.work;
import com.blb.rabbitmq_demo.until.MQUntil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
/**
* 工作队列生产者
* 多对多模式
*/
public class WorkProduct {
public static void main(String[] args) throws IOException {
// 获得连接
Connection connection = MQUntil.getMQConnection();
// 创建通道
Channel channel = connection.createChannel();
// 声明队列
channel.queueDeclare("MyQueues",false,false,false,null);
for (int i = 0; i < 10; i++) {
String msg = "打印了-->" + i;
// 发送消息 交换机名,队列名,手动确认false,配置属性,数据
channel.basicPublish("","MyQueues",false,null,msg.getBytes());
}
channel.close();
connection.close();
}
}
消费者1
package com.blb.rabbitmq_demo.work;
import com.blb.rabbitmq_demo.until.MQUntil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;
import java.io.IOException;
public class WorkConsumer01 {
public static void main(String[] args) throws IOException, InterruptedException {
// 获得连接
Connection connection = MQUntil.getMQConnection();
// 获得通道
Channel channel = connection.createChannel();
// 在队列服务器上声明队列
channel.queueDeclare("MyQueues",false,false,false,null);
// 限制队列一次发一个消息给消费者,等消费者有了反馈,再发下一条
channel.basicQos(1);
// 创建消费者
QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
// 消费者消息通道中的消息 true是自动返回完成状态,false表示手动
channel.basicConsume("MyQueues",false,queueingConsumer);
// 读取消息
while (true){
QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery();
System.out.println(new String(delivery.getBody()));
Thread.sleep(100);
// 消费完消息后手动反馈,处理快的消费者就能处理更多消息
// 手动确定返回状态,不写就是自动确认
channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
}
}
}
消费者2
package com.blb.rabbitmq_demo.work;
import com.blb.rabbitmq_demo.until.MQUntil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;
import java.io.IOException;
public class WorkConsumer02 {
public static void main(String[] args) throws IOException, InterruptedException {
// 获得连接
Connection connection = MQUntil.getMQConnection();
// 获得通道
Channel channel = connection.createChannel();
// 在队列服务器上声明队列
channel.queueDeclare("MyQueues",false,false,false,null);
// 限制队列一次发一个消息给消费者,等消费者有了反馈,再发下一条
channel.basicQos(1);
// 创建消费者
QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
// 消费者消息通道中的消息
channel.basicConsume("MyQueues",false,queueingConsumer);
// 读取消息
while (true){
QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery();
System.out.println(new String(delivery.getBody()));
Thread.sleep(300);
// 消费完消息后手动反馈,处理快的消费者就能处理更多消息
// 手动确定返回状态,不写就是自动确认
channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
}
}
}
上边代码已经实现能者多劳:
-
channel.basicQos(1);限制队列一次发一个消息给消费者,等消费者有了反馈,再发下一条
-
channel.basicAck 消费完消息后手动反馈,处理快的消费者就能处理更多消息
-
basicConsume 中的参数改为false
发布订阅模式
生产者
package com.blb.rabbitmq_demo.publish;
import com.blb.rabbitmq_demo.until.MQUntil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
/**
* 发布和订阅模式生产者
* 消息会通过交换机发送到队列
*/
public class PublishProduct {
public static void main(String[] args) throws IOException {
// 获得连接
Connection connection = MQUntil.getMQConnection();
// 创建通道
Channel channel = connection.createChannel();
// 声明交换机 fanout扇出
channel.exchangeDeclare("MyExchange","fanout");
String msg = "hello fanout";
// 发布到交换机
channel.basicPublish("MyExchange","",null,msg.getBytes());
channel.close();
connection.close();
}
}
消费者1
package com.blb.rabbitmq_demo.publish;
import com.blb.rabbitmq_demo.until.MQUntil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;
import java.io.IOException;
/**
* 发布订阅模式的消费者1
* 两个消费者绑定的消息队列不同
* 通过交换机一个消息能被不同队列的两个消费者同时获取
* 一个队列可以有多个消费者,队列中的消息只能被一个消费者获取
*/
public class PublishConsumer01 {
public static void main(String[] args) throws IOException, InterruptedException {
// 获得连接
Connection connection = MQUntil.getMQConnection();
// 获得通道
Channel channel = connection.createChannel();
// 在队列服务器上声明队列
channel.queueDeclare("MyQueues",false,false,false,null);
// 绑定队列1到交换机上
channel.queueBind("MyQueues","MyExchange","");
QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
channel.basicConsume("MyQueues",true,queueingConsumer);
// 读取消息
while (true){
QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery();
System.out.println("消费者1-->"+new String(delivery.getBody()));
}
}
}
消费者2
package com.blb.rabbitmq_demo.publish;
import com.blb.rabbitmq_demo.until.MQUntil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;
import java.io.IOException;
public class PublishConsumer02 {
public static void main(String[] args) throws IOException, InterruptedException {
// 获得连接
Connection connection = MQUntil.getMQConnection();
// 获得通道
Channel channel = connection.createChannel();
// 在队列服务器上声明队列
channel.queueDeclare("MyQueues1",false,false,false,null);
// 绑定队列1到交换机上
channel.queueBind("MyQueues1","MyExchange","");
QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
channel.basicConsume("MyQueues1",true,queueingConsumer);
// 读取消息
while (true){
QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery();
System.out.println("消费者2-->"+new String(delivery.getBody()));
}
}
}
路由模式
生产者
package com.blb.rabbitmq_demo.direct;
import com.blb.rabbitmq_demo.until.MQUntil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
/**
* 路由模式的生产者,发布消息会有特定的Key,消息会被绑定特定Key的消费者获取
*/
public class DirectProduct {
public static void main(String[] args) throws IOException {
// 获得连接
Connection connection = MQUntil.getMQConnection();
// 创建通道
Channel channel = connection.createChannel();
// 声明交换机 direct
channel.exchangeDeclare("MyExchange2","direct");
String msg = "hello direct";
// 绑定队列1到交换机,指定key为error
channel.queueBind("MyQueues1","MyExchange2","error");
// 绑定队列2到交换机,指定key为error
channel.queueBind("MyQueues2","MyExchange2","debug");
// 发布到交换机
channel.basicPublish("MyExchange2","error",null,msg.getBytes());
channel.basicPublish("MyExchange2","debug",null,msg.getBytes());
channel.close();
connection.close();
}
}
消费者1
package com.blb.rabbitmq_demo.direct;
import com.blb.rabbitmq_demo.until.MQUntil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;
import java.io.IOException;
/**
* 路由模式的消费者1
* 可以指定Key,消费特定的消息
*/
public class DirectConsumer01 {
public static void main(String[] args) throws IOException, InterruptedException {
// 获得连接
Connection connection = MQUntil.getMQConnection();
// 获得通道
Channel channel = connection.createChannel();
// 在队列服务器上声明队列
channel.queueDeclare("MyQueues1",false,false,false,null);
QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
channel.basicConsume("MyQueues1",true,queueingConsumer);
// 读取消息
while (true){
QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery();
System.out.println("消费者1-->"+new String(delivery.getBody()));
}
}
}
消费者2
package com.blb.rabbitmq_demo.direct;
import com.blb.rabbitmq_demo.until.MQUntil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;
import java.io.IOException;
/**
* 路由模式的消费者1
* 可以指定Key,消费特定的消息
*/
public class DirectConsumer02 {
public static void main(String[] args) throws IOException, InterruptedException {
// 获得连接
Connection connection = MQUntil.getMQConnection();
// 获得通道
Channel channel = connection.createChannel();
// 在队列服务器上声明队列
channel.queueDeclare("MyQueues2",false,false,false,null);
QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
channel.basicConsume("MyQueues2",true,queueingConsumer);
// 读取消息
while (true){
QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery();
System.out.println("消费者2-->"+new String(delivery.getBody()));
}
}
}
主题模式
生产者
package com.blb.rabbitmq_demo.topic;
import com.blb.rabbitmq_demo.until.MQUntil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
/**
* 主题模式的生产者
* * 匹配任意一个单词 com.*
* # 匹配.号隔开的多个单词 com.#
*/
public class TopicProduct {
public static void main(String[] args) throws IOException {
// 获得连接
Connection connection = MQUntil.getMQConnection();
// 创建通道
Channel channel = connection.createChannel();
// 声明交换机 direct
channel.exchangeDeclare("MyExchange3","topic");
// 绑定队列1到交换机,指定key
channel.queueBind("MyQueues1","MyExchange3","*.cn");
// 绑定队列2到交换机,指定key
channel.queueBind("MyQueues2","MyExchange3","#.com");
// 发布到交换机
channel.basicPublish("MyExchange3","blb.lg.com",null,"blb.lg.cn".getBytes());
channel.basicPublish("MyExchange3","blb.cn",null,"blb.cn".getBytes());
channel.basicPublish("MyExchange3","blb.com",null,"blb.com".getBytes());
channel.close();
connection.close();
}
}
消费者1
package com.blb.rabbitmq_demo.topic;
import com.blb.rabbitmq_demo.until.MQUntil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;
import java.io.IOException;
/**
* 主题模式的消费者1 ,类似路由模式,可以使用通配符对Key进行筛选
* #匹配1个或多个单词,*匹配一个单词
*/
public class TopicConsumer01 {
public static void main(String[] args) throws IOException, InterruptedException {
// 获得连接
Connection connection = MQUntil.getMQConnection();
// 获得通道
Channel channel = connection.createChannel();
// 在队列服务器上声明队列
channel.queueDeclare("MyQueues1",false,false,false,null);
QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
channel.basicConsume("MyQueues1",true,queueingConsumer);
// 读取消息
while (true){
QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery();
System.out.println("消费者1-->"+new String(delivery.getBody()));
}
}
}
消费者2
package com.blb.rabbitmq_demo.topic;
import com.blb.rabbitmq_demo.until.MQUntil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;
import java.io.IOException;
/**
* 主题模式的消费者2 ,类似路由模式,可以使用通配符对Key进行筛选
* #匹配1个或多个单词,*匹配一个单词
*/
public class TopicConsumer02 {
public static void main(String[] args) throws IOException, InterruptedException {
// 获得连接
Connection connection = MQUntil.getMQConnection();
// 获得通道
Channel channel = connection.createChannel();
// 在队列服务器上声明队列
channel.queueDeclare("MyQueues2",false,false,false,null);
QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
channel.basicConsume("MyQueues2",true,queueingConsumer);
// 读取消息
while (true){
QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery();
System.out.println("消费者2-->"+new String(delivery.getBody()));
}
}
}
总结
模式有:
-
一对一
-
一对多
前两种都只有一个队列,多个消费者共享一个队列中的消息
-
发布订阅模式
由交换机绑定多个队列,每个消费者消费自己的队列中的消息
-
路由模式
在发布订阅模式的基础上,加入路由键,消息通过键路由到不同的队列
-
主题模式
在路由模式基础上,键中加入通配符