RabbitMQ官方提出,RabbitMQ在两种情况下会将消息写入磁盘:

1,消息本身在publish的时候就要求消息写入磁盘;

2,内存不足,需要将内存中的消息转移到磁盘;

 



如何实现持久化(durable)

首先,持久化包括:

1,exchange的持久化

channel.exchangeDeclare(EXCHANGE_NAME, "direct",true);

2,队列的持久化

channel.queueDeclare(queue_name, durable, false, false, null); //声明消息队列,且为可持久化的

3,消息的持久化

channel.basicPublish("", queue_name, MessageProperties.PERSISTENT_TEXT_PLAIN,message.getBytes());

//MessageProperties.PERSISTENT_TEXT_PLAIN即持久化消息设置

注:只有交换器和队列都是持久化的时候才能绑定在一起。

如果使用的是Spring amqp,那么直接在配置里边配置即可。



消息什么时候会写到磁盘

写入文件前会有一个Buffer,大小为1M(1048576),数据在写入文件时,首先会写入到这个Buffer,如果Buffer已满,则会将Buffer写入到文件(未必刷到磁盘);

有个固定的刷盘时间:25ms,也就是不管Buffer满不满,每隔25ms,Buffer里的数据及未刷新到磁盘的文件内容必定会刷到磁盘;

每次消息写入后,如果没有后续写入请求,则会直接将已写入的消息刷到磁盘:使用Erlang的receive x after 0来实现,只要进程的信箱里没有消息,则产生一个timeout消息,而timeout会触发刷盘操作。

消息在磁盘文件中的格式

 

消息保存于在mnesia数据库($MNESIA/msg_store_persistent/x.rdq文件)中,其中x为数字编号,从1开始,每个文件最大为16M(16777216),超过这个大小会生成新的文件,文件编号加1。消息以以下格式存在于文件中:

<<Size:64, MsgId:16/binary, MsgBody>>

MsgId为RabbitMQ通过rabbit_guid:gen()每一个消息生成的GUID,MsgBody会包含消息对应的exchange,routing_keys,消息的内容,消息对应的协议版本,消息内容格式(二进制还是其它)等等。

 



文件何时删除

当所有文件中的垃圾消息(已经被删除的消息)比例大于阈值(GARBAGE_FRACTION = 0.5)时,会触发文件合并操作(至少有三个文件存在的情况下),以提高磁盘利用率。

publish消息时写入内容,ack消息时删除内容(更新该文件的有用数据大小),当一个文件的有用数据等于0时,删除该文件。