消息队列MessageQueue简介及RabbitMQ五大模型总结
WHAT IS MQ
消息队列,即MQ,Message Queue。
消息队列是典型的:生产者、消费者模型。生产者不断向消息队列中生产消息,消费者不断的从队列中获取消息。因为消息的生产和消费都是异步的,而且只关心消息的发送和接收,没有业务逻辑的侵入,这样就实现了生产者和消费者的解耦
AMQP与JMS
MQ是消息通信的模型,并不是具体实现。现在实现MQ的有两种主流方式:AMQP、JMS
两者间的区别和联系:
- JMS是定义了统一的接口,来对消息操作进行统一;AMQP是通过规定协议来统一数据交互的格式,这点有点类似于JSON
- JMS限定了必须使用Java语言;AMQP只是协议,不规定实现方式,因此是跨语言的。
- JMS规定了两种消息模型;而AMQP的消息模型更加丰富
常见的MQ产品有:
- ActiveMQ:基于JMS,老牌MQ产品,集群架构模式丰富
- RocketMQ:基于JMS,阿里巴巴产品,目前交由Apache基金会,对Kafla进行了改写,对消息的可靠传输及事务性做了优化
- Kafka:分布式消息系统,高吞吐量,对消息的重复、丢失、错误没有严格要求,适合产生大量数据的互联网服务的数据收集业务,适合数据冗余处理
- RabbitMQ:基于AMQP协议,erlang语言开发,稳定性好,支持事务,传输可靠
RabbitMQ
RabbitMQ是基于AMQP的一款消息管理系统
官方教程:http://www.rabbitmq.com/getstarted.html
下载与安装详见我的RabbitMQ安装指南
RabbitMQ的官方介绍:
RabbitMQ is a message broker: it accepts and forwards messages. You can think about it as a post office: when you put the mail that you want posting in a post box, you can be sure that Mr. or Ms. Mailperson will eventually deliver the mail to your recipient. In this analogy, RabbitMQ is a post box, a post office and a postman
翻译:
RabbitMQ是一个消息代理:它接受和转发消息。 你可以把它想象成一个邮局:当你把邮件放在邮箱里时,你可以确定邮差先生最终会把邮件发送给你的收件人。 在这个比喻中,RabbitMQ是邮政信箱,邮局和邮递员。
RabbitMQ与邮局的主要区别是它不处理纸张,而是接受,存储和转发数据消息的二进制数据块
RabbitMQ的基本原理:
这是一个基本的生产者消费者模型,在RabbitMQ中,生产者把消息发送到Server中的交换机,而消费者则直接绑定到队列,获取消息即可
在交换机模式下,RabbitMQ可以轻松实现点对点传输与动态路由
RabbitMQ的几个核心角色:
P(producer/ publisher):生产者,一个发送消息的用户应用程序。
C(consumer):消费者,消费和接收有类似的意思,消费者是一个主要用来等待接收消息的用户应用程序
Queue队列(红色区域):rabbitmq内部类似于邮箱的一个概念。虽然消息流经rabbitmq和你的应用程序,但是它们只能存储在队列中。队列只受主机的内存和磁盘限制,实质上是一个大的消息缓冲区。许多生产者可以发送消息到一个队列,许多消费者可以尝试从一个队列接收数据。
总之:
生产者将消息发送到队列,消费者从队列中获取消息,队列是存储消息的缓冲区。
以下是官网给出的几大通信模型
但是第6种其实是RPC通信,并不是MQ,因此不予学习。那么也就剩下5种。
但是其实3、4、5这三种都属于订阅模型,只不过进行路由的方式不同(路由是社么?后面说到)。
最简单的生产者消费者模型
这个是大家耳熟能详的生产者消费者模型了,这个很简单,我们只需要让生产者发送消息,消费者接收到消息就行了
首先引入RabbitMQ的Java客户端依赖
<dependencies>
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.4.3</version>
</dependency>
</dependencies>
生产者代码:
package Simple_PC;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
/**
* 生产者
*/
public class Send {
private final static String QUEUE_NAME = "simple_queue";
public static void main(String[] argv) throws Exception {
//定义连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置服务地址
factory.setHost("192.168.31.111");
// 通过工程获取连接
Connection connection = factory.newConnection();
// 从连接中创建通道,使用通道才能完成消息相关的操作
Channel channel = connection.createChannel();
// 声明(创建)队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 消息内容
String message = "Hello World!!";
// 向指定的队列中发送消息
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
System.out.println(" [x] Sent '" + message + "'");
//关闭通道和连接
channel.close();
connection.close();
}
}
消费者代码:
package Simple_PC;
import com.rabbitmq.client.*;
import java.io.IOException;
public class Recv {
private final static String QUEUE_NAME = "simple_queue";
public static void main(String[] argv) throws Exception {
//定义连接工厂
ConnectionFactory factory = new ConnectionFactory();
/*指定ip*/
factory.setHost("192.168.31.111");
// 通过工程获取连接
Connection connection = factory.newConnection();
// 创建通道
Channel channel = connection.createChannel();
// 声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
//监听消息,监听到消息时自动调用
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), "UTF-8");
System.out.println(" [x] Received '" + message + "'");
};
/*第二个参数为ACK设置,代表收到消息自动将将消息从队列中移除*/
channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> {
});
}
}
启动消费者监听消息,再启动生产者发送消息:
关于消息确认机制ACK
通过刚才的案例可以看出,消息一旦被消费者接收,队列中的消息就会被删除。
那么问题来了:RabbitMQ怎么知道消息被接收了呢?
如果消费者领取消息后,还没执行操作就挂掉了呢?或者抛出了异常?消息消费失败,但是RabbitMQ无从得知,这样消息就丢失了!
因此,RabbitMQ有一个ACK机制。当消费者获取消息后,会向RabbitMQ发送回执ACK,告知消息已经被接收。不过这种回执ACK分两种情况:
- 自动ACK:消息一旦被接收,消费者