rabbitmq中消息的存储

转自:https://blog.csdn.net/yongche_shi/article/details/51500534

1. 大概原理:

所有队列中的消息都以append的方式写到一个文件中,当这个文件的大小超过指定的限制大小后,关闭这个文件再创建一个新的文件供消息的写入。文件名(*.rdq)从0开始然后依次累加。当某个消息被删除时,并不立即从文件中删除相关信息,而是做一些记录,当垃圾数据达到一定比例时,启动垃圾回收处理,将逻辑相邻的文件中的数据合并到一个文件中。

2. 消息的读写及删除:

rabbitmq在启动时会创建msg_store_persistent,msg_store_transient两个进程,一个用于持久消息的存储,一个用于内存不够时,将存储在内存中的非持久化数据转存到磁盘中。所有队列的消息的写入和删除最终都由这两个进程负责处理,而消息的读取则可能是队列本身直接打开文件进行读取,也可能是发送请求由msg_store_persisteng/msg_store_transient进程进行处理。

在进行消息的存储时,rabbitmq会在ets表中记录消息在文件中的映射,以及文件的相关信息。消息读取时,根据消息ID找到该消息所存储的文件,在文件中的偏移量,然后打开文件进行读取。消息的删除只是从ets表删除指定消息的相关信息,同时更新消息对应存储的文件的相关信息(更新文件有效数据大小)。

[java]  view plain  copy
  1. <code class="hljs erlang" style="padding:.5em;margin:0px;display:block;color:rgb(101,123,131);background:rgb(253,246,227);"><span class="hljs-pp" style="padding:0px;margin:0px;"><span class="hljs-keyword" style="padding:0px;margin:0px;color:rgb(133,153,0);">-record</span><span class="hljs-params" style="padding:0px;margin:0px;">(msg_location, <span class="hljs-tuple" style="padding:0px;margin:0px;">{ msg_id,     <span class="hljs-comment" style="padding:0px;margin:0px;color:rgb(147,161,161);">%%消息ID</span>  
  2.                         ref_count,  <span class="hljs-comment" style="padding:0px;margin:0px;color:rgb(147,161,161);">%%引用计数</span>  
  3.                         file,       <span class="hljs-comment" style="padding:0px;margin:0px;color:rgb(147,161,161);">%%消息存储的文件名</span>  
  4.                         offset,     <span class="hljs-comment" style="padding:0px;margin:0px;color:rgb(147,161,161);">%%消息在文件中的偏移量</span>  
  5.                         total_size  <span class="hljs-comment" style="padding:0px;margin:0px;color:rgb(147,161,161);">%%消息的大小</span>  
  6.                       }</span>)</span></span>.  
  7.   
  8. <span class="hljs-pp" style="padding:0px;margin:0px;"><span class="hljs-keyword" style="padding:0px;margin:0px;color:rgb(133,153,0);">-record</span><span class="hljs-params" style="padding:0px;margin:0px;">(file_summary, <span class="hljs-tuple" style="padding:0px;margin:0px;">{ file,       <span class="hljs-comment" style="padding:0px;margin:0px;color:rgb(147,161,161);">%%文件名</span>  
  9.                         valid_total_size, <span class="hljs-comment" style="padding:0px;margin:0px;color:rgb(147,161,161);">%%文件有效数据大小</span>  
  10.                         left,       <span class="hljs-comment" style="padding:0px;margin:0px;color:rgb(147,161,161);">%%位于该文件左边的文件</span>  
  11.                         right,      <span class="hljs-comment" style="padding:0px;margin:0px;color:rgb(147,161,161);">%%位于该文件右边的文件</span>  
  12.                         file_size,  <span class="hljs-comment" style="padding:0px;margin:0px;color:rgb(147,161,161);">%%文件总的大小</span>  
  13.                         locked,     <span class="hljs-comment" style="padding:0px;margin:0px;color:rgb(147,161,161);">%%上锁标记 垃圾回收时防止对文件进行操作</span>  
  14.                         readers     <span class="hljs-comment" style="padding:0px;margin:0px;color:rgb(147,161,161);">%%当前读文件的队列数</span>  
  15.                       }</span>)</span></span></code>  

3. 垃圾回收:

由于执行消息删除操作时,并不立即对在文件中对消息进行删除,也就是说消息依然在文件中,仅仅是垃圾数据而已。当垃圾数据超过一定比例后(默认比例为50%),并且至少有三个及以上的文件时,rabbitmq触发垃圾回收。垃圾回收会先找到符合要求的两个文件(根据#file_summary{}中left,right找逻辑上相邻的两个文件,并且两个文件的有效数据可在一个文件中存储),然后锁定这两个文件,并先对左边文件的有效数据进行整理,再将右边文件的有效数据写入到左边文件,同时更新消息的相关信息(存储的文件,文件中的偏移量),文件的相关信息(文件的有效数据,左边文件,右边文件),最后将右边的文件删除。


4. 性能考虑:

(1)操作引用计数(flying_ets)

队列在进行消息的写入和删除操作前,会在flying_ets表里通过+1,-1的方式进行计数,然后投递请求给msg_store_persistent/msg_store_transient进程进行处理,进程在真正写操作或者删除之前会再次判断flying_ets中对应消息的计数决定是否需要进行相应操作。这样,对于频繁写入和删除的操作,概率减少实际的写入和删除。

[java]  view plain  copy
  1. <code class="hljs r" style="padding:.5em;margin:0px;display:block;color:rgb(101,123,131);background:rgb(253,246,227);">client_write(MsgId, Msg, Flow,  
  2.              CState=<span class="hljs-comment" style="padding:0px;margin:0px;color:rgb(147,161,161);">#client_msstate{cur_file_cache_ets=CurFileCacheEts,</span>  
  3.                                     client_ref=CRef}) ->  
  4.     ok = client_update_flying(+<span class="hljs-number" style="padding:0px;margin:0px;color:rgb(42,161,152);">1</span>, MsgId, CState),  
  5.     ok = update_msg_cache(CurFileCacheEts, MsgId, Msg),  
  6.     ok = server_cast(CState, {write, CRef, MsgId, Flow}).  
  7.   
  8. remove(MsgIds, CState = <span class="hljs-comment" style="padding:0px;margin:0px;color:rgb(147,161,161);">#client_msstate { client_ref = CRef }) -></span>  
  9.     [client_update_flying(-<span class="hljs-number" style="padding:0px;margin:0px;color:rgb(42,161,152);">1</span>, MsgId, CState) || MsgId <- MsgIds],  
  10.     server_cast(CState, {remove, CRef, MsgIds}).  
  11.   
  12. client_update_flying(Diff, MsgId,  
  13.                      <span class="hljs-comment" style="padding:0px;margin:0px;color:rgb(147,161,161);">#client_msstate{flying_ets = FlyingEts,</span>  
  14.                                      client_ref = CRef}) ->  
  15.     Key = {MsgId, CRef},  
  16.     case ets:insert_new(FlyingEts, {Key, Diff}) of  
  17.         true  ->  
  18.             ok;  
  19.         false ->  
  20.             <span class="hljs-keyword" style="padding:0px;margin:0px;color:rgb(133,153,0);">try</span> ets:update_counter(FlyingEts, Key, {<span class="hljs-number" style="padding:0px;margin:0px;color:rgb(42,161,152);">2</span>, Diff}) of  
  21.             <span class="hljs-keyword" style="padding:0px;margin:0px;color:rgb(133,153,0);">...</span>  
  22.     end.  
  23.   
  24. handle_cast({write, CRef, MsgId, Flow},  
  25.             State = <span class="hljs-comment" style="padding:0px;margin:0px;color:rgb(147,161,161);">#msstate{cur_file_cache_ets=CurFileCacheEts,</span>  
  26.                              clients=Clients}) ->  
  27.     <span class="hljs-keyword" style="padding:0px;margin:0px;color:rgb(133,153,0);">...</span>  
  28.     true = <span class="hljs-number" style="padding:0px;margin:0px;color:rgb(42,161,152);">0</span> =< ets:update_counter(CurFileCacheEts, MsgId, {<span class="hljs-number" style="padding:0px;margin:0px;color:rgb(42,161,152);">3</span>, -<span class="hljs-number" style="padding:0px;margin:0px;color:rgb(42,161,152);">1</span>}),  
  29.     case update_flying(-<span class="hljs-number" style="padding:0px;margin:0px;color:rgb(42,161,152);">1</span>, MsgId, CRef, State) of  
  30.         process ->  
  31.             [{MsgId,Msg,_PWC}]=ets:lookup(CurFileCacheEts, MsgId),  
  32.             noreply(write_message(MsgId, Msg, CRef, State));  
  33.         ignore ->  
  34.             <span class="hljs-keyword" style="padding:0px;margin:0px;color:rgb(133,153,0);">...</span>  
  35.     end;  
  36.   
  37. handle_cast({remove, CRef, MsgIds}, State) ->  
  38.     {RemovedMsgIds, State1} =  
  39.         lists:foldl(  
  40.             fun (MsgId, {Removed, State2}) ->  
  41.                 case update_flying(+<span class="hljs-number" style="padding:0px;margin:0px;color:rgb(42,161,152);">1</span>, MsgId, CRef, State2) of  
  42.                     process ->  
  43.                         {[MsgId | Removed],  
  44.                         remove_message(MsgId, CRef, State2)};  
  45.                     ignore ->  
  46.                         {Removed, State2}  
  47.                 end  
  48.             end, {[], State}, MsgIds),  
  49.     <span class="hljs-keyword" style="padding:0px;margin:0px;color:rgb(133,153,0);">...</span>  
  50.   
  51. update_flying(Diff,MsgId,CRef,<span class="hljs-comment" style="padding:0px;margin:0px;color:rgb(147,161,161);">#msstate{flying_ets = FlyingEts }) -></span>  
  52.     Key = {MsgId, CRef},  
  53.     NDiff = -Diff,  
  54.     case ets:lookup(FlyingEts, Key) of  
  55.         [] ->  
  56.             ignore;  
  57.         [{_,  Diff}] ->  
  58.             ignore;  
  59.         [{_, NDiff}] ->  
  60.             ets:update_counter(FlyingEts, Key, {<span class="hljs-number" style="padding:0px;margin:0px;color:rgb(42,161,152);">2</span>, Diff}),  
  61.             true = ets:delete_object(FlyingEts, {Key, <span class="hljs-number" style="padding:0px;margin:0px;color:rgb(42,161,152);">0</span>}),  
  62.             process;  
  63.         [{_, <span class="hljs-number" style="padding:0px;margin:0px;color:rgb(42,161,152);">0</span>}] ->  
  64.             true = ets:delete_object(FlyingEts, {Key, <span class="hljs-number" style="padding:0px;margin:0px;color:rgb(42,161,152);">0</span>}),  
  65.             ignore;  
  66.         [{_, Err}] ->  
  67.             throw({bad_flying_ets_record, Diff, Err, Key})  
  68.     end.</code>  

(2)尽可能的并发读

在读取消息的时候,都先根据消息ID找到对应存储的文件,如果文件存在并且未被锁住,则直接打开文件,从指定位置读取消息的内容。

如果消息存储的文件被锁住了,或者对应的文件不存在了,则发送请求,由msg_store_persistent/msg_store_transient进程进行处理。

(3)消息缓存

1)利用ets表进行缓存 

对于当前正在写的文件,所有消息在写入前都会在cur_file_cache_ets表中存一份,消息读取时会优先从这里进行查找。文件关闭时,会将cur_file_cache_ets表中引用计数为0的消息进行清除。

2)file_handle_cache的写缓存

rabbitmq中对文件的操作封转到了file_handle_cache模块,以写模式打开文件时,默认有1M大小的缓存,即在进行文件的写操作时,是先写入到这个缓存中,当缓存超过大小或者显式刷新,才将缓存中的内容刷入磁盘中。

[java]  view plain  copy
  1. <code class="hljs handlebars" style="padding:.5em;margin:0px;display:block;color:rgb(101,123,131);background:rgb(253,246,227);"><span class="xml" style="padding:0px;margin:0px;">rabbit_msg_store.erl  
  2.   
  3. -define(HANDLE_CACHE_BUFFER_SIZE, 1048576). %% 1MB  
  4.   
  5. open_file(Dir, FileName, Mode) ->  
  6.     file_handle_cache:open(form_filename(Dir, FileName),  
  7.                            ?BINARY_MODE ++ Mode,  
  8.                            [{write_buffer,?HANDLE_CACHE_BUFFER_SIZE}]).  
  9.   
  10. file_handle_cache.erl  
  11.   
  12. append(Ref,Data) ->  
  13.     with_handles(  
  14.         [Ref],  
  15.         fun ([#handle { is_write = false }]) ->  
  16.            {error, not_open_for_writing};  
  17.         ([Handle]) ->  
  18.             case maybe_seek(eof, Handle) of  
  19.                 </span><span class="hljs-expression" style="padding:0px;margin:0px;">{{<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">ok</span>, _<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Offset</span>}, <span class="hljs-begin-block" style="padding:0px;margin:0px;">#handle</span>{<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">hdl</span> = <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Hdl</span>,  
  20.                                         <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">offset</span> = <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Offset</span>,  
  21.                                         <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">write</span>_<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">buffer</span>_<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">size</span>_<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">limit</span> = 0,  
  22.                                         <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">at</span>_<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">eof</span> = <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">true</span> }= <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Handle</span>1} <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">-</span>>  
  23.                     <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Offset</span>1 = <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Offset</span> + <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">iolist</span>_<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">size</span>(<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Data</span>),  
  24.                     {<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">prim</span>_<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">file</span>:<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">write</span>(<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Hdl</span>, <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Data</span>),  
  25.                     [<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Handle</span>1<span class="hljs-begin-block" style="padding:0px;margin:0px;">#handle</span>{<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">is</span>_<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">dirty</span>=<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">true</span>,<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">offset</span>=<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Offset</span>1 }]};  
  26.                 {{<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">ok</span>, _<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Offset</span>},<span class="hljs-begin-block" style="padding:0px;margin:0px;">#handle</span>{<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">write</span>_<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">buffer</span> = <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">WriteBuffer</span>,  
  27.                                        <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">write</span>_<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">buffer</span>_<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">size</span> = <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Size</span>,  
  28.                                        <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">write</span>_<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">buffer</span>_<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">size</span>_<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">limit</span>= <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Limit</span>,  
  29.                                        <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">at</span>_<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">eof</span> = <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">true</span> } = <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Handle</span>1} <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">-</span>>  
  30.                     <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">WriteBuffer</span>1 = [<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Data</span> | <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">WriteBuffer</span>],  
  31.                     <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Size</span>1 = <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Size</span> + <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">iolist</span>_<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">size</span>(<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Data</span>),  
  32.                     <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Handle</span>2=<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Handle</span>1<span class="hljs-begin-block" style="padding:0px;margin:0px;">#handle</span>{<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">write</span>_<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">buffer</span>=<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">WriteBuffer</span>1,  
  33.                                            <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">write</span>_<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">buffer</span>_<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">size</span>=<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Size</span>1},  
  34.                     <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">case</span> <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Limit</span> =/= <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">infinity</span> <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">andalso</span> <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Size</span>1 > <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Limit</span> <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">of</span>  
  35.                         <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">true</span>  <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">-</span>>  
  36.                             {<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Result</span>,<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Handle</span>3} = <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">write</span>_<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">buffer</span>(<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Handle</span>2),  
  37.                             {<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Result</span>, [<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Handle</span>3]};  
  38.                         <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">false</span> <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">-</span>>  
  39.                             {<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">ok</span>, [<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Handle</span>2]}  
  40.                     <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">end</span>;  
  41.                 {{<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">error</span>, _} = <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Error</span>, <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Handle</span>1} <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">-</span>>  
  42.                     {<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Error</span>, [<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">Handle</span>1]}  
  43.             <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">end</span>  
  44.         <span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">end</span>)<span class="hljs-variable" style="padding:0px;margin:0px;color:rgb(181,137,0);">.</span></span></code>  
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值