文章目录
生产者端
同一服务下,维护生产者在一个vhost下的connection连接和channel,会断线重连
由于TCP的开启和关闭都是昂贵的开销,而且会存在慢启动的问题,不能一上来就传送大量的数据。假设每次发送消息都要重新建立一个connection,那如果同一时间发送消息的频率过于频繁,或者消息量过大,就可能出现异常(可用for循环连发消息1000次验证:1,每次都建立connection和channel,2,建立一次connection,每次发消息都建立channel,3,建立一次connection和channel,直接发送消息)。
因此,每个服务在启动时,即建立好所有vhost下的connection连接和channel。当需要发送消息的时候,直接用对应的channel去声明exchange和Publish即可。connection和channel在服务器运行的过程中不会主动close,当服务发生异常时会尝试主动close。
持久化和ack
从生产者发送消息到rabbitmq把消息发给队列,过程中的每个时刻都可能丢消息。
首先,为了确保rabbitmq不丢消息,需要持久化queue的元信息和队列中的消息。前者需要在assertExchange的时候讲durabel设为true,后者需要把publish方法里options里的persistent设为true(或者把deliveryMode设为2,但persistent的优先级高于deliveryMode)。这样,RabbitMQ就会将持久化消息写入磁盘上的持久化日志文件,如果rabbitmq挂了之后再次重启,也会恢复queue和这个queue里的数据。当然,写入磁盘有开销,持久化会降低rabbitmq的性能,但可以有效防止rabbitmq挂了之后丢消息。可以根据项目需求来灵活运用,只对内容重要、难以找回,不易再次触发的消息开启持久化。
其次,要处理生产者丢消息的情况。一般有两种做法,使用rabbitmq事务,或者引入confirm机制。事务是同步的,confirm机制是异步的。前者会大大降低性能,根据rabbitmq的官网,吞吐量会以250倍的因子减少。故而后者是通常推荐的做法。
引入confirm机制后,对于可路由消息,当所有队列都接受消息时,rabbitmq将发送basic.ack给生产者。对于路由到持久队列的持久性消息,这意味着持久化到磁盘。目前前端所有的消息都是routed的,即是知道要发送到哪个路由的。
开启持久化后会有延迟。如果没有空闲的队列,RabbitMQ消息会存储在一段时间(几百毫秒)之后批量传递消息到磁盘,以最小化同步的消息数。这意味着在恒定负载下,basic.ack的延迟可以达到几百毫秒。因此,需要异步发送消息,并尽量把发消息作为方法中的最后一环,从而不会block接下来的行为。
尽量对所有的生产者都开启confirm机制,因为它本身是异步的,对性能影响较小。
var open = require(