目录
RabbitMQ几大组件
- 生产者:消息创建者,将消息发送到消息中间件的。
- 消息:包括有效载荷与标签。有效载荷:要传输的数据;标签:描述有效载荷的属性;RabbitMQ通过标签决定谁获得该消息,消费者只能得到有效载荷。
- 消费者:是接收消息的。
- Brocker:是消息中间件服务的节点。一个Brocker=一个RabbitMQ,一个服务器上如果有多个RabbitMQ就有多个Brocker。
- 队列:是用来存储消息的。
- 交换器:交换器仅仅对消息进行转发。交换器有不同的类型(如:fanout、direct、topic、headers)
- 路由键:交换器与队列的关系是通过绑定键(BindingKey,称为路由键)建立的。
- 绑定
交换器类型
RabbitMQ常用的交换器类型有fanout、direct、topic、headers四种:
- fanout:它会把所有发送到该交换器的消息,路由到所有与该交换器绑定的队列中;
public static void main(String[] args) throws Exception { ConnectionFactory factory = new ConnectionFactory(); factory.setUsername("rabbitstudy"); factory.setPassword("123456"); factory.setHost("192.168.0.1"); factory.setPort(5672); Connection connection = factory.newConnection(); Channel channel = connection.createChannel(); channel.exchangeDeclare(EXCHANGE_NAME, "fanout"); channel.queueDeclare(QUEUE_NAME1, false, false, false, null); channel.queueDeclare(QUEUE_NAME2, false, false, false, null); channel.queueBind(QUEUE_NAME1, EXCHANGE_NAME, "com.cdsn.test1"); channel.queueBind(QUEUE_NAME2, EXCHANGE_NAME, "com.cdsn.test2"); channel.basicPublish(EXCHANGE_NAME, "com.cdsn.test", MessageProperties.PERSISTENT_TEXT_PLAIN, "Hello World!".getBytes()); channel.close(); connection.close(); }
- direct:把消息路由到那些BindingKey和RoutingKey完全匹配的队列中;
- topic:类似于direct,但可以使用通配符匹配规则;("#"可匹配多个或零个单词,“*”可匹配单个单词)
public static void main(String[] args) throws Exception { ConnectionFactory factory = new ConnectionFactory(); factory.setUsername("rabbitstudy"); factory.setPassword("123456"); factory.setHost("192.168.0.1"); factory.setPort(5672); Connection connection = factory.newConnection(); Channel channel = connection.createChannel(); channel.exchangeDeclare(EXCHANGE_NAME, "topic"); channel.queueDeclare(QUEUE_NAME1, false, false, false, null); channel.queueDeclare(QUEUE_NAME2, false, false, false, null); channel.queueBind(QUEUE_NAME1, EXCHANGE_NAME, "*.test"); channel.queueBind(QUEUE_NAME2, EXCHANGE_NAME, "#.test"); channel.basicPublish(EXCHANGE_NAME, "com.cdsn.test", MessageProperties.PERSISTENT_TEXT_PLAIN, "Hello World!".getBytes()); channel.close(); connection.close(); }
- header:消息不根据路由键的匹配规则路由,而是根据发送的消息内容中的headers属性进行匹配。
RabbitMQ运行流程
- 生产者发送消息
-
生产者与Broker建立连接(Connection),开启信道(Channel)
-
生产者声明交换器(交换器类型、是否持久化、是否自动删除等)
-
生产者声明队列(是否持久化、是否排他、是否自动删除)
-
生产者通过路由键将交换器和队列绑定
-
生产者发送消息至Broker(携带路由键等)
-
交换器根据接收到的路由键,以及交换器类型查找匹配的队列
-
找到,将消息存入相对应的队列中;找不到,则根据生产者的配置,选择丢弃还是退回给生产者
-
关闭信道、关闭连接
- 消费者接收消息
- 消费者与Broker建立连接(Connection),开启信道(Channel)
- 消费者向Broker请求消费相应队列的消息,可能设置回调函数
- 等待Broker回应并投递相应队列中的消息,接收消息
- 消费者确认(ack)接收到的消息
- RabbitMQ从队列中删除相应已经被确认的消息
- 关闭信道、关闭连接
Connection与Channel
RabbitMQ所有的AMQP指令都是通过信道完成的。
RabbitMQ的Channel与Netty中的NIO模型区别在于Channel是建立在TCP连接之上的。
生产者运行流程
- 当生产者与Broker建立连接时,调用factory.newConnection()方法。
- 当客户端调用channel.createChannel()方法时,准备开启通道。
- 发送消息时,调用channel.basicPublish()方法。
消费者运行流程
- 当客户端调用channel.basicConsume()方法时,向Broker告知准备好消费消息。
- 客户端调用channel.basicAck()方法,向Broker发送确认通知。
细说交换器
(一)交换器
参数 | 作用 |
exchange | 交换器名称 |
type | 交换器的类型,常见如fanout、direct、topic、headers |
durable | 设置是否持久化 |
autoDelete | 设置是否自动删除 |
internal | 设置是否是内置的 |
argument | 其他结构化参数 |
备注:
持久化:交换器的创建持久化到磁盘;
自动删除:自动删除的前提是至少有一个队列或者交换器与这个交换器绑定,之后所有与这个交换器绑定的队列或者交换器都与此解绑;
channel.exchangeDeclare(EXCHANGE_NAME, "direct", false, true, false, null);// 创建一个自动删除的交换器
内置交换器:客户端程序无法直接发送消息到交换器,只能通过交换器路由到内置交换器。
(1)不等待服务器通知创建队列成功命令:
void channel.exchangeDeclareNoWait(String exchange,String type, boolean durable, boolean autoDelete, boolean internal, Map<String, Object> arguments) throws IOException;
(2)检测交换器是否存在,如果不存在则抛出异常:
Exchange.DeclareOK exchangeDeclarePassive(String name) throws IOException;// 如果要用到这方法,需要解决这个异常
(3)删除交换器:
① Exchange.DeleteOK exchangeDelete(String exchange) throws IOException;
② void exchangeDeleteNoWait(String exchange, boolean ifUnused) throws IOException;
③ Exchange.DeleteOK exchangeDelete(String exchange, boolean ifUnused) throws IOException;
备注:
exchange标识交换器的名称
ifUnused用来设置是否在交换器没有被使用的情况下删除
(二)队列
声明队列:
(1)queueDeclare();
(2)queueDeclare(String queue,String exchange,String type, boolean durable, boolean autoDelete, boolean internal, Map<String, Object> arguments);
备注:排他队列,如果一个队列被声明,该队列仅对首次声明它的连接可见,并在连接断开时自动删除。这种队列适用于一个客户端同时发送和读取消息的应用场景。
1、排他队列是基于连接(Connection)可见的,同一个连接的不同信道(Channel)是可以同时访问同一连接创建的排他队列;
2、“首次”是指如果一个连接已经声明了一个排他队列,其他连接是不允许建立同名的排他队列的,这个与普通队列不同;
3、即使该队列是持久化的,一旦连接关闭或者客户端退出,该排他队列都会被自动删除。
参数 | 作用 |
---|---|
x-message-ttl | 定义消息过期时间,单位ms。 |
x-expires | 定义队列过期时间,单位ms; 当队列在指定时间内未被使用,自动删除该队列。 |
x-max-length | 定义队列最大长度,队列中消息最大个数; 默认消息长度是不限制的; |
x-max-length-bytes | 限定队列最大占用的空间大小,单位B。 |
x-dead-letter-exchange | 当队列消息长度大于最大长度、或者过期等等,将从队列中删除的消息推送到指定的交换机中。 |
x-dead-letter-routing-key | 设置将删除的消息推送到指定交换机的路由键,不设置则使用原来的路由键。 |
x-max-priority | 优先级队列。 |
x-queue-mode | 先将消息保存到磁盘上,不放在内存中,当消费者开始消费的时候才加载到内存中。 |
(1)不等待服务器通知创建队列成功:
void queueDeclareNoWait(String queue,boolean durable,boolean exclusive,boolean autoDelete,Map<String,Object> arguments) throws IOException;
(2)检测队列是否存在,如果不存在则抛出异常:
Exchange.DeclareOk queueDeclarePassive(String queue) throws IOException;
(3)删除队列:
- Queue.De1eteOk queueDelete(String queue) throws IOException;
- Queue.De1eteOk queueDelete(String queue, boolean ifUnused, boolean ifEmpty) throws IOException;
- void queueDeleteNoWait(String queue, boolean ifUnused, boolean ifEmpty) throws IOException;
(4)清空队列:
Queue.PurgeOK queuePurge(String queue) throws IOException;
(三)交换器与队列
交换器与队列绑定
(1)Queue.BindOK queueBind(String queue, String exchange, String routingKey) throws IOExceptino;
(2)Queue.BindOK queueBind(String queue, String exchange, String routingKey, Map<String, Object> arguments) throws IOException;
(3)void queueBindNoWait(String queue, String exchange, String routingKey, Map<String, Object> arguments) throws IOException;
解除绑定:
(1)Queue.UnbindOK queueUnbind(String queue, String exchange, String routingKey) throws IOException;
(2)Queue.UnbindOK queueUnbind(String queue, String exchange, String routingKey, Map<String, Object> arguments) throws IOException;
参数 | 作用 |
queue | 队列的名称 |
exchange | 交换器名称 |
routingKey | 用来绑定队列和交换器的路由键 |
argument | 定义绑定的一些参数 |
(四)交换器与交换器
交换器与交换器绑定:
(1)Exchange.BindOK exchangeBind(String destination, String source, String routingKey) throws IOExceptino;
(2)Exchange.BindOK exchangeBind(String destination, String source, String routingKey, Map<String, Object> arguments) throws IOExceptino;
(3)void exchangeBind(String destination, String source, String routingKey, Map<String, Object> arguments) throws IOExceptino;