本文将介绍一些MQ中常见的概念,同时也会简单实现一下RabbitMQ的工作流程。
MQ概念
Message Queue消息队列。是用来存储消息的队列,多用于分布式系统之间的通信。
系统间调用通常有:同步通信和异步通信。MQ就是在异步通信的时候使用的。
同步通信:
异步通信:
MQ作用
- 异步解耦:在业务流程中,一些操作可能非常耗时,但并不需要即时返回结果。可以借助消息队列(MQ)将这些操作异步化。例如,当用户注册后,可以将发送注册短信或邮件通知作为异步任务处理,而不必等待这些操作完成后才告知用户注册成功。
- 流量削峰:在访问量剧增的情况下,应用仍然需要继续发挥作用,但这样的突发流量并不常见。如果以能处理这类峰值为标准而投入资源,必然是巨大的浪费。使用MQ可以使关键组件支撑突发访问压力,不会因为突发流量而崩溃。例如,秒杀或促销活动中,可以使用MQ来控制流量,将请求排队,系统根据自己的处理能力逐步处理这些请求。
- 消息分发:当多个系统需要对同一数据做出响应时,可以使用MQ进行消息分发。例如,支付成功后,支付系统可以向MQ发送消息,其他系统订阅该消息,而无需轮询数据库。
- 延迟通知:在需要在特定时间后发送通知的场景中,可以使用MQ的延迟消息功能。例如,在电子商务平台中,如果用户下单后一定时间内未支付,可以使用延迟队列在超时后自动取消订单。
AMQP
AMQP(Advanced Message Queuing Protocol)是一个高级消息队列协议,定义了一系列消息交换功能,包括交换器(Exchange)和队列(Queue)。这些组件共同工作,使得生产者可以将消息发送到交换器,由队列接收并等待消费者取走。AMQP还定义了一个网络协议,允许客户端应用通过该协议与消息代理和AMQP模型进行交互通信。RabbitMQ遵循AMQP协议,使用Erlang实现,并支持其他协议如STOMP和MQTT。可以说AMQP模型和RabbitMQ模型结构一致。
RabbitMQ
功能完备: MQ 功能较为全面,几乎支持所有主流语言。
开源友好: 界面非常友好,易于使用。
性能: 性能良好,吞吐量可达到万级。
社区活跃度: 社区活跃度较高。
适用场景: 适合中小型公司,数据量和并发量较小的场景
Producer和Consumer
Producer(生产者):是RabbitMQ Server的客户端,向RabbitMQ发送消息。消息通常带有业务逻辑结构的数据,例如JSON字符串,并可能包含标签,RabbitMQ根据标签路由消息。
Consumer(消费者):也是RabbitMQ Server的客户端,从RabbitMQ接收消息。在消费过程中,标签会被丢弃,消费者只收到消息,且无需知道消息的生产者。
Broker(消息代理):即RabbitMQ Server,负责接收和收发消息。通常,一个RabbitMQ Broker可以视为一个RabbitMQ服务节点或实例,大多数情况下等同于一台RabbitMQ服务器。
Connection和Channel
Connection(连接):是客户端与RabbitMQ服务器之间的TCP连接。这是建立消息传递的基础,负责传输客户端和服务器之间的所有数据和控制信息。
Channel(通道/信道):是在Connection之上的一个抽象层。在RabbitMQ中,一个TCP连接可以有多个Channel,每个Channel都是独立的虚拟连接。消息的发送和接收都是基于Channel进行的。通道的主要作用是将消息的读写操作复用到同一个TCP连接上,从而减少建立和关闭连接的开销,提高性能。
Virtual host
Virtual Host(虚拟主机):这是一个虚拟概念,为消息队列提供逻辑上的隔离机制。在RabbitMQ中,一个Broker Server上可以存在多个Virtual Host。它允许不同的用户在同一RabbitMQ服务器上创建和管理独立的交换机(exchange)、队列(queue)等,类似于MySQL中的数据库(database)。
Queue
Queue: 队列, 是RabbitMQ的内部对象, 用于存储消息。
Exchange
Exchange(交换机):是消息到达RabbitMQ Broker的第一站。它负责接收生产者发送的消息,并根据特定的规则将这些消息路由到一个或多个队列(Queue)中。Exchange的主要作用是消息路由,它根据类型和规则决定如何转发接收到的消息。
交换机类型
RabbitMQ中的交换机有四种主要类型,每种类型有不同的路由策略:
- Fanout:将消息广播到所有绑定到该交换机的队列,无视路由键。
- Direct:根据消息的路由键将消息精确路由到与交换机绑定的队列。
- Topic:使用通配符模式匹配路由键,将消息路由到匹配的队列。
- Headers:基于消息头部的属性进行路由,路由键不会被使用。(很少使用)
AMQP协议中还有另外两种交换机类型:
System:用于内部系统交换和管理,不用于常规的消息路由。
自定义(Custom):用户可以定义自己的交换机类型,以满足特定的需求。
根据交换机类型的不同,创建了许多不同的工作模式。
Routing Key(路由键)
定义: 路由键是一个字符串,通过它生产者在将消息发送到交换器时告诉交换器如何处理该消息。
作用: 决定了消息的路由路径,即消息应发送到哪个队列或哪些队列。
Binding Key(绑定键)
定义: 在 RabbitMQ 中,通过 Binding(绑定)将交换器与队列关联时,通常会指定一个 Binding Key。
作用: 引导 RabbitMQ 知道如何将消息路由到相应的队列。这个键通常与路由键相关联,从而实现消息的过滤和分发。
通常来说路由键和绑定键没什么区别
工作流程
Producer(生产者) 产生了一条消息。
Producer 连接到 RabbitMQ Broker,建立一个连接(Connection),并开启一个信道(Channel)。
Producer 声明一个交换机(Exchange),用于路由消息。
Producer 声明一个队列(Queue),用于存放消息。
Producer 发送消息至 RabbitMQ Broker。
RabbitMQ Broker 接收消息,并将其存入相应的队列(Queue)中。如果未找到相应的队列,则根据生产者的配置,选择丢弃或退回消息给生产者。
工作模式
RabbitMQ提供了七个工作模式。查看文档即可:RabbitMQ Tutorials | RabbitMQ
工作模式图中的字母:
P(生产者):也就是要发送消息的程序。
C(消费者):消费者,消息的接收者。
Queue(消息队列):缓存消息;生产者将消息投递到其中,消费者从中取出消息。
Exchange(交换机X):按照规则分发消息。
相关通用代码提取到Constants类中
public class Constants {
// 主机ip
public static final String HOST = "ip地址";
// rabbitmq服务器端口号 默认是5672
public static final int PORT = 5672;
// rabbitmq用户名
public static final String USER_NAME = "用户名";
// rabbitmq用户密码
public static final String PASSWORD = "用户密码";
// rabbitmq虚拟主机
public static final String VIRTUAL_HOST = "虚拟主机名";
// 简单模式
public static final String SIMPLE_QUEUE = "simple.queue";
// 工作队列模式
public static final String WORK_QUEUE = "work.queue";
// 发布订阅模式
public static final String FANOUT_EXCHANGE = "fanout.exchange";
public static final String FANOUT_QUEUE1 = "fanout.queue1";
public static final String FANOUT_QUEUE2 = "fanout.queue2";
// 路由模式
public static final String DIRECT_EXCHANGE = "direct.exchange";
public static final String DIRECT_QUEUE1 = "direct.queue1";
public static final String DIRECT_QUEUE2 = "direct.queue2";
// 通配符模式
public static final String TOPIC_EXCHANGE = "topic.exchange";
public static final String TOPIC_QUEUE1 = "topic_queue1";
public static final String TOPIC_QUEUE2 = "topic_queue2";
// rpc 模式
public static final String RPC_REQUEST_QUEUE = "rpc.request.queue";
public static final String RPC_RESPONSE_QUEUE = "rpc.response.queue";
// publisher confirms
public static final String PUBLISHER_CONFIRMS_QUEUE1 = "publisher.confirms.queue1";
public static final String PUBLISHER_CONFIRMS_QUEUE2 = "publisher.confirms.queue2";
public static final String PUBLISHER_CONFIRMS_QUEUE3 = "publisher.confirms.queue3";
// 推拉模式
public static final String MESSAGE_QUEUE = "message.queue";
}
简单模式
特点: 一个生产者 P 和一个消费者 C,消息只能被消费一次。这种模式也称为点对点(Point-to-Point)模式。
适用场景: 当消息只能被单个消费者处理时。
生产者代码
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import rabbitmq.common.Constants;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
// 1. 创建链接
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(Constants.HOST);
factory.setPort(Constants.PORT);
factory.setUsername(Constants.USER_NAME);
factory.setPassword(Constants.PASSWORD);
factory.setVirtualHost(Constants.VIRTUAL_HOST);
Connection connection = factory.newConnection();
// 2. 开启信道
Channel channel = connection.createChannel();
// 3. 声明交换机 简单模式使用内置交换机 所以不用声明
// 4. 声明队列
/*
* queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete,
* Map<String, Object> arguments)
* 参数说明:
* queue: 队列名称
* durable: 可持久化
* exclusive: 是否独占
* autoDelete: 是否自动删除
* arg