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

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的实现:

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里即返回。





时间: 2024-10-06 03:25:32

Erlang:RabbitMQ源码分析 4. file_handle_cache实现分析的相关文章

[erlang]proc_lib源码浅析

源码位置位于安装目录的lib/stdlib/src下. 之前在使用gen_server时,由于之前自己实现过一个gen_server,因此对它内部的机制也能知道个七七八八,最近在用erlang的fsm模块,突然想读一读它得源码,这才突然发现erlang的源码内部还是做了很复杂的工作,尤其是有个"阴魂不散"的模块proc_lib. 无论是gen_server也好,gen_fsm也好,实际上在start的时候,都是调用的底层gen模块的start/start_link函数,由start函数

老李推荐:第6章5节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览-事件

老李推荐:第6章5节<MonkeyRunner源码剖析>Monkey原理分析-事件源-事件源概览-事件 从网络过来的命令字串需要解析翻译出来,有些命令会在翻译好后直接执行然后返回,但有一大部分命令在翻译后需要转换成对应的事件,然后放入到命令队列里面等待执行.Monkey在取出一个事件执行的时候主要是执行其injectEvent方法来注入事件,而注入事件根据是否需要往系统注入事件分为两种: 需要通过系统服务往系统注入事件:如MonkeyKeyEvent事件会通过系统的InputManager往系

老李推荐:第6章3节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览-命令翻译类

老李推荐:第6章3节<MonkeyRunner源码剖析>Monkey原理分析-事件源-事件源概览-命令翻译类 每个来自网络的字串命令都需要进行解析执行,只是有些是在解析的过程中直接执行了事,而有些是需要在解析后创建相应的事件类实例并添加到命令队列里面排队执行.负责这部分工作的就是命令翻译类.那么我们往下还是继续在MonkeySourceNetwork这个范畴中MonkeyCommand类是怎么一回事: 图6-3-1 MonkeyCommand族谱 图中间的MonkeyCommand是一个接口,

Erlang:RabbitMQ源码分析 3. supervisor和supervisor2深入分析

supervisor也是Erlang/OTP里一个常用的behavior,用于构建supervisor tree实现进程监控,故障恢复. 而RabbitMQ实现了一个supervisor2,我们从源码角度分析二者的实现和区别. 先介绍一些supervisor的基本概念,假设node_manager_sup是一个supervisor,它的init函数会定义supervisor的一些参数和它的children. 参数: 1. Restart Strategy: Strategy必须是simple_o

第5章2节《MonkeyRunner源码剖析》Monkey原理分析-启动运行: 启动流程概览(原创)

天地会珠海分舵注:本来这一系列是准备出一本书的,详情请见早前博文"寻求合作伙伴编写<深入理解 MonkeyRunner>书籍".但因为诸多原因,没有如愿.所以这里把草稿分享出来,所以错误在所难免.有需要的就参考下吧,转发的话还请保留每篇文章结尾的出处等信息. 每个应用都会有一个入口方法来供操作系统调用执行,Monkey这个应用的入口方法就是在Monkey.java这个类里面的,也就是说Monkey.java就是整个Monkey应用的入口类. Monkey作为一个命令行应用,

第5章5节《MonkeyRunner源码剖析》Monkey原理分析-启动运行: 获取系统服务引用(原创)

天地会珠海分舵注:本来这一系列是准备出一本书的,详情请见早前博文"寻求合作伙伴编写<深入理解 MonkeyRunner>书籍".但因为诸多原因,没有如愿.所以这里把草稿分享出来,所以错误在所难免.有需要的就参考下吧,转发的话还请保留每篇文章结尾的出处等信息. 上一节我们描述了monkey的命令处理入口函数run是如何调用optionProcess方法来解析命令行参数的.启动参数主要时去指导Monkey时怎么运行起来的,但Monkey作为MonkeyRunner框架的一部分,

doker 1.12-runc源码逻辑跳转流程分析

入口1–>runc处理(2)中处理-->至libcontainer处理(3) 第一步runc代码处理 checkpoint checkpointCommand(main.go) -> checkpointCommand(checkpoint.go) container createCommand(main.go)->createCommand(create.go)->startContainer(untils_linux.go)->run(untils_linux.go

从源码的角度带你分析Glide整体加载流程以及设计模式

基本调用流程 这一篇文章我们从源码的角度分析Glide实现,首先我们从一句最简单的使用方式来探索他的设计与实现,可以看到下面这句话是最基础的使用 Glide.with(this) .load(R.raw.large_giphy_logo) .into(giphyLogoView); 接下来我们一步一步的跟踪他的调用过程,首先我们看到他调用了Glide的with方法并传入了自己的引用,我们可以看到这方法的实现如下: public static RequestManager with(Activit

HashMap与HashTable源码学习及效率比较分析

一.个人学习后的见解: 首先表明学习源码后的个人见解,后续一次依次进行分析: 1.线程安全:HashMap是非线程安全的,HashTable是线程安全的(HashTable中使用了synchronized关键字进行控制),HashMap对应的线程安全的有concurrentHashMap,但如果不用concurrentHashMap的话,也可以只用Collections.synchronizedMap(Map)进行转换. 2.key值为null时的不同处理方式:HashMap允许key值为nul