官网:https://www.rabbitmq.com/getstarted.html
RabbitMQ实现了AMQP协议
角色:
Broker:RabbitMQ服务器(负责消息的存储转发),默认端口5672
Connection:TCP长连接,生产者或消费者和Broker之间建立的通道
Channel:
如果所有的生产者发送消息和消费者接收消息,都直接创建和释放TCP长连接的话, 对于Broker来说肯定会造成很大的性能损耗,也会浪费时间。
所以在AMQP里面引入了 Channel的概念,它是一个虚拟的连接。我们把它翻译 成通道,或者消息信道。这样我们就可以在保持的TCP长连接里面去创建和释放
Channel,大大了减少了资源消耗。
不同的Channel是相互隔离的,每个Channel都有自己的编号。对于每个客户端线
程来说,Channel就没必要共享了,各自用自己的Channel。
另外一个需要注意的是,Channel是RabbitMQ原生API里面的最重要的编程接口,
也就是说我们定义交换机、队列、绑定关系,发送消息,消费消息,调用的都是Channel 接口上的方法。
Queue:
在Broker上有一个对象用来存储消息,在RabbitMQ里面这个对象叫做Queue。
实际上RabbitMQ是用数据库来存储消息的,这个数据库跟RabbitMQ 一样是用Erlang 开发的,名字叫Mnesia。我们可以在磁盘上找到Mnesia的存储路径。
Consumer
消息到底是Broker推送给消费者的?还是消费者主动获取的?消费者消费消息有两
种模式。
一种是Pull模式,对应的方法是basicGet消息存放在服务端,只有消费者主动获
取才能拿到消息。如果每隔一段时间获取一次消息,消息的实时性会降低。但是好处是 可以根据自己的消费能力决定获取消息的频率。
另一种是Push模式,对应的方法是basicConsume只要生产者发消息到服务器, 就马上推送给消费者,消息保存在客户端,实时性很高,如果消费不过来有可能会造成 消息积压。Spring AMQP是push方式,通过事件机制对队列进行监听,只要有消息到 达队列,就会触发消费消息的方法。
RabbitMQ 中 pull 和 push 都有实现。kafka 和 RocketMQ 只有 pull。
由于队列有FIFO的特性,只有确定前一条消息被消费者接收之后,Broker才会把 这条消息从数据库删除,继续投递下一条消息。
一个消费者是可以监听多个队列的,一个队列也可以被多个消费者监听。
但是在生产环境中,我们一般是建议一个消费者只处理一个队列的消息。如果需要 提升处理消息的能力,可以增加多个消费者。这个时候消息会在多个消费者之间轮询。
Exchange
现在我们来思考一个问题,如果要把一条消息发送给多个队列,被多个消费者消费,
应该怎么做?生产者是不是必须要调用多次basicPublish的方法,依次发送给多个队 列?就像消息推送的这种场景,有成干上万个队列的时候,对生产者来说压力太大了。
有没有更好的办法呢?其实,RabbitMQ已经为我们考虑到了这一点,它设计了一 个帮我们路由消息的组件,叫做Exchange。
也就是说,不管有多少个队列需要接收消息,我都只需要发送到Exchange就OK 了,由它帮我来分发。Exchange是不会存储消息的,它只做一件事情,根据规则分发消 息。
那么,Exchange和这些需要接收消息的队列必须建立一个绑定关系,并且为每个队
列指定一个特殊的标识。
Exchange和队列是多对多的绑定关系,也就说,一个交换机的消息一个路由给多个 队列,一个队列也可以接收来自多个交换机的消息。
绑定关系建立好之后,生产者发送消息到Exchange,也会携带一个特殊的标识。当 这个标识跟绑定的标识匹配的时候,消息就会发给一个或者多个符合规则的队列。
Vhost
我们每个需要实现基于RabbitMQ的异步通信的系统,都需要在Broker上创建自己要用到的交换机、队列和它们的绑定关系。如果某个业务系统不想跟别人混用一个 Broker,怎么办?再采购一台硬件服务器单独安装一个RabbitMQ服务?这种方式成本太高了。在同一个硬件服务器上安装多个RabbitMQ的服务呢?比如再运行一个5673 的端口?
没有必要这样做,因为RabbitMQ也考虑到了这一点,设计了虚拟主机VHOST。
VHOST除了可以提高硬件资源的利用率之外,还可以实现资源的隔离和权限的控
制。它的作用类似于编程语言中的namespace和package,不同的VHOST中可以有 同名的Exchange和Queue,它们是完全透明的。
这个时候,我们可以为不同的业务系统创建专属于他们自己的VHOST,然后再为他
们创建专属的用户,给用户分配对应的VHOST的权限。比如给风控系统的用户分配风控 系统的VHOST的权限,这个用户可以访问里面的交换机和队列。给超级管理员分配所有
VHOST的权限。
我们安装RabbitMQ的时候会自带一个默认的VHOST,名字是”/“ 。
我们说到RabbitMQ引入Exchange是为了实现消息的灵活路由,至U底有哪些路由 方式?
- Direct 直连
一个队列与直连类型的交换机绑定,需指定一个明确的绑定键(binding key)。
生产者发送消息时会携带一个路由键(routing key)。 - Topic 主题
"#"代表0个或者多个单词
*代表不多不少一个单词
单词(word)指的是用英文的点〃.“隔开的字符。例如a.bcdef是3个单词。 - Fanout 广播
RabbitMQ进阶知识
1、 怎么实现订单延迟关闭?
RabbitMQ本身不支持延迟投递,总的来说有2种实现方案:
1、先存储到数据库,用定时任务扫描
2、利用 RabbitMQ 的死信队列 (Dead Letter Queue)实现(队列在创建的时候可以指定一个死信交换机,当消息过期(过期时间设置为订单关闭的延时时间)未消费会发送到死信交换机,消费者监听死信交换机消费过期消息)
使用死信队列实现延时消息的缺点:
1) 如果统一用队列来设置消息的TTL,当梯度非常多的情况下,比如1分钟,2 分钟,5分钟,10分钟,20分钟,30分钟 .....需要创建很多交换机和队列来路由消息。
2) 如果单独设置消息的TTL,则可能会造成队列中的消息阻塞一一前一条消息没 有出队(没有被消费),后面的消息无法投递(比如第一条消息过期TTL是30min,第 二条消息TTL是10mino 10分钟后,即使第二条消息应该投递了,但是由于第一条消息 还未出队,所以无法投递)。
3) 可能存在一定的时间误差。
在RabbitMQ 3.5.7及以后的版本提供了一个插件 (rabbitmq-delayed-message-exchange)来实现延时队列功能(Linux 和 Windows 都可用)。同时插件依赖Erlang/OPT18.0及以上。
除了消息过期,还有什么情况消息会变成死信?
- 消息被消费者拒绝并且未设置重回队列:(NACK || Reject) && requeue== false
- 队列达到最大长度,超过了 Max length (消息数)或者Max length bytes (字 节数),最先入队的消息会被发送到DLX。
网友评论