本系列是「RabbitMQ实战:高效部署分布式消息队列」书籍的总结笔记。
前两篇介绍了RabbitMQ在可用性、监控方面的考虑,这是基础保障,因为在某些场景下是不容许丢失消息的,但它和性能往往是对立的,需要根据业务场景做取舍。
当处理一些敏感数据时,比如银行卡信息,需要考虑安全性问题,上一篇总结了数据传输安全方面的知识点,这里就比较好理解了。
通过介绍,你会了解到:
- 对速度的考虑
- 对内存和进程的考虑
- 对安全的考虑
对速度的考虑
有很多因素影响RabbitMQ投递消息的速度,包括消息持久化、路由算法、绑定数目、以及消息确认策略等,下面分别来介绍。
消息持久化
当发布消息时,需要决定丢失其中的任何消息是否可以接受,如果可以接受,可以将delivery-model设置为1,消息就不会持久化到硬盘了。
消息确认
当消费消息时,可以在队列订阅时,通过设定no-ack标记加快消息投递,如果设置为true,服务器就会在消息发送给客户端后自动将其出队。
这样,处理完消息之后就无须再发送确认消息回服务器了,能极大地加快消费者消费消息,但由于某些原因连接中断了,或客户端应用程序发生故障了,消息就永远消息了。
路由算法和绑定规则
前面介绍了3种类型的交换器:direct、fanout、topic,每种交换器代表了服务器实现的特定路由算法,会根据消息的路由键以及队列与交换器之间的绑定来选择队列。
在服务器端,交换器和绑定作为记录条目存储在Mnesia数据库中,当匹配消息路由键时,会尝试查找对应路由键的绑定。
fanout交换器在路由消息的时候,会忽略路由键,不需要进行查找。direct只有一个绑定,也会比较快,topic存储的路由信息比较复杂,由于路由键可以包含以点分隔的多个词,所以匹配消息路由键不仅仅是简单字符串的匹配,也会占用更多内存。
RabbitMQ实现了trie树数据结构用来存储绑定路由键模式,以支持快速查询,关于这种数据结构,我之前没接触过,据说比桶状哈希表还快,后面专门写一篇介绍这个数据结构吧。
投递消息
在交换器找到消息需要路由的目的地之后,会将目的地列表返回给rabbit_router,之后会将消息的副本投递到每一个目的地,如果发布的消息中mandatory和immediate标记设置为false,这个过程会以异步方式执行,从客户端角度看,服务器会变得很快,否则会同步投递。
当mandatory标志位设置为true时,如果exchange根据自身类型和消息routeKey无法找到一个符合条件的queue,那么会调用basic.return方法将消息返还给生产者,当mandatory设为false时,出现上述情形broker会直接将消息扔掉。
当immediate标志位设置为true时,如果exchange在将消息route到queue(s)时发现对应的queue上没有消费者,那么这条消息不会放入队列中。当与消息routeKey关联的所有queue(一个或多个)都没有消费者时,该消息会通过basic.return方法返还给生产者。
假如找到了投递的队列且有消费者准备好接收消息,如果队列为空,消息会直接发送给消费者,不会经过队列这一步,会极大提升速度,所以制定容量规划并计算消息的进出率时,应尽可能让队列保持为空,如果消费滞后导致队列填满的化,服务器会收到内存告警,并将消息刷出磁盘。
还有个参数要注意:auto-ack,消费者接收到消息后,会立刻确认消息,而不用等到逻辑处理好。

网友评论