Erlang:RabbitMQ源码分析 4. file_handle_cache实现分析,erlangrabbitmq


RabbitMQ的文件操作使用file_handle_cache,将Erlang的prim_file Module包了一层。增加了writeBuffer和对文件打开数量的控制逻辑


file_handle_cache也是一个gen_server2,但一般来说只有Open操作会send message to gen_server2, 读写操作,包括writeBuffer都在Client Process里执行和维护。


file_handle_cache里存储了几张表:

   1. Elders:    {Pid, EldestUnusedSince},  存储每个client的pid 和其最老的Handle打开时间, 没有key

   2. Clients:   #cstate,存储每个client的信息,key是pid


在file_handle_cache中可以看出,内存数据在Erlang中的存储无非于几种:

   1. ETS表,例如Elders, Clients; 适合存储一些集中的信息,不同的Process都能访问

   2. Process Dictionary,本Process的信息,例如file_handle_cache中client Process的file和handle

   3. gen_server的state,放在loop state里不断循环反复


下面我们从四个最基本的文件操作函数看一下file_handle_cache的实现:

open:

    1. file_handle_cache对于同一个文件是共享读但不共享写的,所以在client的Process Dictionary里存储了<key = {Path, fhc_file}, value = #file{ reader_count, has_writer}>,当open时如果发现has_writer = TRUE, open的model又是write,就直接返回{error, writer_exists};

    2. 新建一个closed的Handle,Handle相关的信息存储在client的Process Dictionary里 <key = {Ref, fhc_handle}, value = #handle>

    3. file_handle_cache在client的Process Dictionary里存储里一个gb_tree,用来保存这个client打开的所有Handle的打开时间。这个时间是用来调整Client Open的Handle个数。

    1-3都是在client的Process里做的

    4. gen_server:call open,  进入到file_handle_cache的Process loop里 , 当发现file_handle_cache打开的文件数不超过用户预设的文件数时,就update Clients table 和State,返回 OK。

    5. 回到client Process里,调用prim_file:open打开文件,所以打开文件的操作实际上市在Client Process里做的。

    6. 如果4中发现file_handle_cache打开的文件数超过用户预设的文件数时,

        6.1 如果这个pid已经打开了很多文件,就返回close, client接到close后就soft_close所有之前打开的Handle,所谓软关闭,就是将Handle对应文件buffer写完,sync后再关闭。软关闭所有之前打开的Handle后再重新试图open 这个Handle。

        6.2 如果这个pid没有打开任何文件,就对其他的pid下手,计算每个pid的最老时间(最早的打开的文件时间),累加算平均值。

             一般来说client在打开文件前会call register_callback 注册一个清理函数

             6.2.1 如果平均值大于两秒,说明有很多老文件还未关闭, 就对每个client call 清理函数。参数是最老时间的平均值。如果没有注册清理函数就不管。

             6.2.1 如果平均值小于两秒,说明没有很多未关闭的老文件。找到所有注册清理函数的pid, 假设有N个Open被Block,就用其中N个清理函数去清理N个已打开的Handle


close:

      1. 删掉ProcessDictionary 里的{Ref, fhc_handle}

      2. Handle的writebuffer 要prim_file:write

      3. 如果之前有write没有sync,call prim_file:sync

      4. call prim_file:close

      5. 将这个Handle的时间从gb_tree里删掉

      6. 更新Process Dictionary里的{Path, fhc_file}, read_count, write_count之类


read:  read本身没有什么特殊的,也没有用buffer

      1.  虽然已经Open过,但有可能因为打开文件过多被close,所以还是看下,如果被close了要reopen一下

      2.  Handle的writebuffer要prim_file:write

      3.  调用prim_file:read得到结果


append:

      1.  和read一样,也是有可能会reopen。

      2.  如果client在Open时没有设置WriteBuffer,就直接call prim_file:write(Hdl, Data)了事

      3.  所谓的Writebuff是针对于单个Handle的,即Open时搞一个空的List来做Buffer,append时如果BufferSize > Client Open时设置的limit,就call prim_file:write把Buffer都写到file里,如果BufferSize < limit,就只是将Data放到Buffer里即返回。

  

怎在一台机器上配置多个RabbitMQ

Rabbitmq的配置文件有三个,位于/etc/rabbitmq/, 这三个文件分别是:  (1)enabled_plugins, 设置允许的插件列表,该配置文档的格式为erlang的列表格式如 [rabbitmq_management,rabbitmq_visualiser].  (2)rabbitmq.conf,设置rabbitmq的运行参数。该配置文件中的每个参数为一个erlang的 tuple,结构为{Key,Value}, Key为atom类型, Value为一个term。其中几个关键参数为:  tcp_listerners设置rabbimq的监听端口,默认为[5672]。  disk_free_limit 磁盘低水位线,若磁盘容量低于指定值则停止接收数据,默认值为 {mem_relative, 1.0},即与内存相关联1:1,也可定制为多少byte.  vm_memory_high_watermark,设置内存低水位线,若低于该水位线,则开启流控机制,默认值是0.4,即内存总量的40%。  hipe_compile 将部分rabbimq代码用High Performance Erlang compiler编译,可提升性能,该参数是实验性,若出现erlang vm segfaults,应关掉。
 


相关内容

    暂无相关文章