RabbitMQ 官方地址:http://www.rabbitmq.com/
RabbitMQ 的基础架构图
- Broker:接收和分发消息的应用,RabbitMQ Server 就是Message Broker
- Virtual host:出于多租户和安全因素设计的,把AMQP 的基本组件划分到一个虚拟的分组中,类似于网络中的namespace 概念。当多个不同的用户使用同一个RabbitMQserver 提供的服务时, 可以划分出多个vhost, 每个用户在自己的vhost 创建exchange/queue 等
- Connection:publisher/consumer 和broker 之间的TCP 连接
- Channel:如果每一次访问RabbitMQ 都建立一个Connection,在消息量大的时候建立TCP Connection 的开销将是巨大的,效率也较低。Channel 是在connection 内部建立的逻辑连接,如果应用程序支持多线程,通常每个thread 创建单独的channel 进行通讯,AMQP method 包含了channel id 帮助客户端和message broker 识别channel,所以channel 之间是完全隔离的。Channel 作为轻量级的Connection 极
大减少了操作系统建立TCP connection 的开销 - Exchange:message 到达broker 的第一站,根据分发规则,匹配查询表中的routing key,分发消息到queue 中去。常用的类型有:direct (point-to-point),topic (publish-subscribe) and fanout (multicast)
- Queue:消息最终被送到这里等待consumer 取走
- Binding:exchange 和queue 之间的虚拟连接,binding 中可以包含routing key。Binding 信息被保存到exchange 中的查询表中,用于message 的分发依据
RabbitMQ 的六种工作模式
RabbitMQ 提供了6 种模式:
- 简单模式
- Work queues 模式
- Publish/Subscribe 发布与订阅模式
- Routing 路由模式
- Topics 主题模式
- RPC 远程调用模式(远程调用,不太算MQ)
RabbitMQ 配置环境
rabbitmq 命令存放在:/usr/lib/rabbitmq/lib/rabbitmq_server-3.8.1/sbin/
启用管理插件rabbitmq-plugins enable rabbitmq_management #开启管理界面
命令:
systemctl start rabbitmq-server.service #启动服务
systemctl status rabbitmq-server.service
systemctl restart rabbitmq-server.service
systemctl stop rabbitmq-server.service
防火墙放开15672 端口号:
firewall-cmd --add-port=15672/tcp --permanent
firewall-cmd --reload
RabbitMQ图形化界面
overview:概览
- connections:无论生产者还是消费者,都需要与RabbitMQ 建立连接后才可以完成消息
的生产和消费,在这里可以查看连接情况 - channels:通道,建立连接后,会形成通道,消息的投递获取依赖通道。
- Exchanges:交换机,用来实现消息的路由
- Queues:队列,即消息队列,消息存放在队列中,等待消费,消费后被移除队列。
端口: - 5672: rabbitMq 的编程语言客户端连接端口
- 15672:rabbitMq 管理界面端口
- 25672:rabbitMq 集群的端口
Users
- 超级管理员(administrator)
可登陆管理控制台,可查看所有的信息,并且可以对用户,策略(policy)进行操作。 - 监控者(monitoring)
可登陆管理控制台,同时可以查看rabbitmq 节点的相关信息(进程数,内存使用情况,磁
盘使用情况等),但不能管理用户 - 策略制定者(policymaker)
可登陆管理控制台, 同时可以对policy 进行管理。但无法查看节点的相关信息(上图红框
标识的部分)。 - 普通管理者(management)
仅可登陆管理控制台,无法看到节点信息,也无法对策略进行管理。 - 其他
无法登陆管理控制台,通常就是普通的生产者和消费者。
- 命令行添加新用户:
增加管理员账号:rabbitmqctl add_user admin admin
给账号分配角色:rabbitmqctl set_user_tags admin administrator
修改账号密码:rabbitmqctl change_password admin admin
查看用户列表:rabbitmqctl list_users
Start ->>>RabbitMQ Tutorials
消息确认机制
消息一旦被消费者接收,队列中的消息就会被删除。
那么问题来了:RabbitMQ 怎么知道消息被接收了呢?
如果消费者领取消息后,还没执行操作就挂掉了呢?或者抛出了异常?消息消费失败,但是
RabbitMQ 无从得知,这样消息就丢失了!
因此,RabbitMQ 有一个ACK 机制。当消费者获取消息后,会向RabbitMQ 发送回执ACK,
告知消息已经被接收。不过这种回执ACK 分两种情况:
- 自动ACK:消息一旦被接收,消费者自动发送ACK
- 手动ACK:消息接收后,不会发送ACK,需要手动调用
大家觉得哪种更好呢?
这需要看消息的重要性: - 如果消息不太重要,丢失也没有影响,那么自动ACK 会比较方便
- 如果消息非常重要,不容丢失。那么最好在消费完成后手动ACK,否则接收消息后
就自动ACK,RabbitMQ 就会把消息从队列中删除。如果此时消费者宕机,那么消
息就丢失了。
Work queues 工作队列模式
工作队列,又称任务队列。主要思想就是避免执行资源密集型任务时,必须等待它执行完成。
相反我们稍后完成任务,我们将任务封装为消息并将其发送到队列。在后台运行的工作进
程将获取任务并最终执行作业。当你运行许多消费者时,任务将在他们之间共享,但是一个
消息只能被一个消费者获取
- Work Queues:与入门程序的简单模式相比,多了一个或一些消费端,多个消费端共同消
费同一个队列中的消息。 - 应用场景:对于任务过重或任务较多情况使用工作队列可以提高任务处理的速度。
- 面试题:避免消息堆积?
1)采用work queue,多个消费者监听同一队列。
2)接收到消息以后,而是通过线程池,异步消费。
能者多劳
在比较慢的消费者创建队列后我们可以使用basicQos 方法和prefetchCount = 1 设
置。这告诉RabbitMQ 一次不要向工作人员发送多于一条消息。或者换句话说,不要向
工作人员发送新消息,直到它处理并确认了前一个消息。相反,它会将其分派给不是仍然
忙碌的下一个工作人员
小结
- 在一个队列中如果有多个消费者,那么消费者之间对于同一个消息的关系是竞争的关
系。 - Work Queues 对于任务过重或任务较多情况使用工作队列可以提高任务处理的速度。
例如:短信服务部署多个,只需要有一个节点成功发送即可。
Publish/Subscribe
一次同时向多个消费者发送消息,一条消息可以被多个消费者消费
而在订阅模型中,多了一个exchange 角色,而且过程略有变化:
- P:生产者,也就是要发送消息的程序,但是不再发送到队列中,而是发给X(交换机) 即,
1)声明Exchange,不再声明Queue
2)发送消息到Exchange,不再发送到Queue - C:消费者,消息的接受者,会一直等待消息到来。
- Queue:消息队列,接收消息、缓存消息。
- Exchange:交换机,图中的X。一方面,接收生产者发送的消息。另一方面,知道如何处理消息,例如递交给某个特别队列、递交给所有队列、或是将消息丢弃。到底如何操作,取决于Exchange 的类型。
Exchange 有常见以下3 种类型=>>
- Fanout:广播,将消息交给所有绑定到交换机的队列
- Direct:定向,把消息交给符合指定routing key 的队列
- Topic:通配符,把消息交给符合routing pattern(路由模式) 的队列
Exchange(交换机)只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与Exchange 绑定,或者没有符合路由规则的队列,那么消息会丢失!在广播模式下,消息发送流程是这样的==>>
- 可以有多个消费者
- 每个消费者有自己的queue(队列)
- 每个队列都要绑定到Exchange(交换机)
- 生产者发送的消息,只能发送到交换机,交换机来决定要发给哪个队列,生产者无法决定。
- 交换机把消息发送给绑定过的所有队列
- 队列的消费者都能拿到消息。实现一条消息被多个消费者消费
Fanout 交换机解读
说明:
- 队列在绑定到交换机的时候不需要指定routing key
- 发送消息的时候也不需要指定routing key
- 凡是发送给交换机的消息都会广播发送到所有与交换机绑定的队列中。
发布订阅模式与工作队列模式的区别
- 工作队列模式不用定义交换机,而发布/订阅模式需要定义交换机。
- 发布/订阅模式的生产方是面向交换机发送消息,工作队列模式的生产方是面向队列发
送消息(底层使用默认交换机)。 - 发布/订阅模式需要设置队列和交换机的绑定,工作队列模式不需要设置,实际上工作
队列模式会将队列绑定到默认的交换机。
Routing 路由模式
有选择性的接收消息
路由模式特点:
- 队列与交换机的绑定,不能是任意绑定了,而是要指定一个
RoutingKey
(路由key) - 消息的发送方在向Exchange 发送消息时,也必须指定消息的
RoutingKey
。 - Exchange 不再把消息交给每一个绑定的队列,而是根据消息的
Routing Key
进行
判断,只有队列的Routingkey
与消息的Routing key
完全一致,才会接收到消息
图解: - P:生产者,向Exchange 发送消息,发送消息时,会指定一个routing key。
- X:Exchange(交换机),接收生产者的消息,然后把消息递交给与routing key完全匹配的队列
- C1:消费者,其所在队列指定了需要routing key 为error 的消息
- C2:消费者,其所在队列指定了需要routing key 为info、error、warning 的消息
在编码上与Publish/Subscribe 发布与订阅模式
的区别是交换机的类型为:
Direct,还有队列绑定交换机的时候需要指定routing key。
小结
Routing 模式要求队列在绑定交换机时要指定routing key , 消息会转发到符合
routing key 的队列。
Topics 通配符模式
//支持 通配符
Topic 类型与Direct 相比,都是可以根据RoutingKey
把消息路由到不同的队列。只
不过Topic
类型Exchange
可以让队列在绑定Routing key
的时候使用通配符!
Routingkey 一般都是有一个或多个单词组成,多个单词之间以”.”分割,例如:
item.insert
通配符规则:
#
:匹配一个或多个词
*
:匹配不多不少恰好1 个词
举例:
item.#
:能够匹配item.insert.abc
或者item.insert
item.*
:只能匹配`item.insert
图解:
- 红色Queue:绑定的是usa.#,因此凡是以usa.开头的routing key 都会被匹配到
- 黄色Queue:绑定的是#.news ,因此凡是以.news 结尾的routing key 都会被匹配
-
小结
Topic 主题模式可以实现Publish/Subscribe 发布与订阅模式
和Routing 路 由模式
的功能;只是Topic 在配置routing key 的时候可以使用通配符,显得更加灵活。
持久化
如何避免消息丢失?
1)消费者的ACK 机制。可以防止消费者丢失消息。
2)但是,如果在消费者消费之前,MQ 就宕机了,消息就没了。
是可以将消息进行持久化呢?
要将消息持久化,前提是:队列、Exchange 都持久化
- 交换机持久化
durable = true
exchangeDeclare(String exchange, String type, boolean durable)
- 队列持久化
durable = true
queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
- 消息持久化
props=MessageProperties.PERSISTENT_TEXT_PLAIN
basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)
模式总结
RabbitMQ 工作模式:
- 简单模式HelloWorld
一个生产者、一个消费者,不需要设置交换机(使用默认的交换机) - 工作队列模式Work Queue
一个生产者、多个消费者(竞争关系),不需要设置交换机(使用默认的交换机) - 发布订阅模式Publish/subscribe
需要设置类型为fanout 的交换机,并且交换机和队列进行绑定,当发送消息到交换机后,交换机会将消息发送到绑定的队列 - 路由模式Routing
需要设置类型为direct 的交换机,交换机和队列进行绑定,并且指定routing key,当发送消息到交换机后,交换机会根据routing key 将消息发送到对应的队列 - 通配符模式Topic
需要设置类型为topic 的交换机,交换机和队列进行绑定,并且指定通配符方式的routing key,当发送消息到交换机后,交换机会根据routing key 将消息发送到对应的队列
(- -笔记整理)
扩展: