五种消息模型
RabbitMQ提供了6种消息模型,但是第6种其实是RPC,并不是MQ,因此不予学习。那么也就剩下5种。
simple简单模式
work工作模式(资源的竞争)
一个生产者,多个消费者,每个消费者获取到的消息唯一
publish/subscribe发布订阅(共享资源)
一个生产者发送的消息会被多个消费者获取。
routing路由模式
发送消息到交换机并且要指定路由key ,消费者将队列绑定到交换机时需要指定路由key
topic 主题模式(路由模式的一种)
将路由键和某模式进行匹配,此时队列需要绑定在一个模式上,“#”匹配一个词或多个词,“*”只匹配一个词。
我们通过代码工程来了解RabbitMQ的工作方式:
依赖:
<properties>
<java.version>1.8</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.target>${java.version}</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.7.3</version>
</dependency>
</dependencies>
连接工具类
public class ConnectionUtil {
public static Connection GetRabbitConnection() throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername("guest");
factory.setPassword("guest");
factory.setVirtualHost("/");
factory.setHost("192.168.70.136");
factory.setPort(5672);
Connection conn = null;
return factory.newConnection();
}
}
simple简单模式
生产者发送消息
public class RabbitmqPublisherDemo {
public static void main(String[] args) throws IOException, TimeoutException {
publisher();
}
public static void publisher() throws IOException, TimeoutException {
// 创建一个连接
Connection conn = ConnectionUtil.GetRabbitConnection();
if (conn != null) {
try {
// 创建通道
Channel channel = conn.createChannel();
for (int i = 0; i < 10; i++) {
// 声明(创建)队列,必须声明队列才能够发送消息,我们可以把消息发送到队列中。
// 声明一个队列是幂等的 - 只有当它不存在时才会被创建
channel.queueDeclare("simple-queue", false, false, false, null);
String content = String.format(i+" 当前时间:%s", new Date().getTime());
// 发送内容
// 参数一:交换机名称
// 参数二:队列名称
// 参数三:消息的其他属性
// 参数四:消息主体
channel.basicPublish("", "simple-queue", null, content.getBytes("UTF-8"));
System.out.println("已发送消息:" + content);
}
// 关闭连接
channel.close();
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
运行
管理工具中查看消息
进入队列页面,可以看到新建了一个队列:simple_queue
点击队列名称,进入详情页,可以查看消息:
在控制台查看消息并不会将消息消费,所以消息还在。
消费者获取消息
public class RabbitmqConsumerDemo {
public static void main(String[] args) throws IOException, TimeoutException {
consumer();
}
public static void consumer() throws IOException, TimeoutException {
// 创建一个连接
Connection conn = ConnectionUtil.GetRabbitConnection();
if (conn != null) {
try {
// 创建通道
final Channel channel = conn.createChannel();
// 声明队列【
// 参数一:队列名称
// 参数二:是否持久化
// 参数三:是否独占模式
// 参数四:消费者断开连接时是否删除队列
// 参数五:消息其他参数
channel.queueDeclare("demo-queue", false, false, false, null);
// 创建订阅器,并接受消息
channel.basicConsume("demo-queue", true, "", new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
byte[] body) throws IOException {
// 队列名称
String routingKey = envelope.getRoutingKey();
// 内容类型
String contentType = properties.getContentType();
// 消息正文
String content = new String(body, "utf-8");
System.out.println("消息正文:" + content);
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
运行,然后观察浏览器界面的队列,会发现消息已经没有了,同时,消费者虽然已经获取了消息,但是程序没有停止,一直在监听队列中是否有新的消息。一旦有新的消息进入队列,就会立即打印.
消息确认机制(ACK)
在刚才的示例中,消息一旦被消费者接收,队列中的消息就会被删除。那RabbitMQ怎么知道消息被接收了呢?
如果消费者领取消息后,还没执行操作就挂掉了呢?或者抛出了异常?消息消费失败,但是RabbitMQ无从得知,这样消息就丢失了!
因此,RabbitMQ有一个ACK机制。当消费者获取消息后,会向RabbitMQ发送回执ACK,告知消息已经被接收。不过这种回执ACK分两种情况:
- 自动ACK:消息一旦被接收,消费者自动发送ACK
- 手动ACK:消息接收后,不会发送ACK,需要手动调用
根据消息的重要性来选择:
- 如果消息不太重要,丢失也没有影响,那么自动ACK会比较方便
- 如果消息非常重要,不容丢失。那么最好在消费完成后手动ACK,否则接收消息后就自动ACK,RabbitMQ就会把消息从队列中删除。如果此时消费者宕机,那么消息就丢失了。
如果要手动ACK,需要改动我们的代码:
public class RabbitmqConsumerDemo {
public static void main(String[] args) throws IOException, TimeoutException {