flume-TailSource分析

来源:互联网 发布:怎么找淘宝刷手 编辑:程序博客网 时间:2024/04/29 01:10
TailSource
首先看一下tail这种source的使用接口,下面是flume-0.9.4的文档,
tail("filename"[, startFromEnd=false]{,delim="regex", delimMode="exclude|prev|next"}) :: Similar to Unix’s tail utility. One line is one event. Generates events for the entire file then stays open for more data, and follows filename. (e.g. if tailing file "foo" and then "foo" is moved to "bar" and a new file appears named "foo", it will finish reading the new "bar" file and then start from the beginning of "foo"). If the startFromEnd parameter is false, tail will re-read from the beginning of the file. If it is true, it will only start reading from the current end of file. If the last line of a file does not end with a newline character (\n), the tail source will only send an event with this last line when the tail is closed. See the section on tailing a file for details on delim and delimMode.

我们可以看出它最多可以由四个参数,最少要设置一个。
下面是TailSource的源码分析,首先看一下TailSource这个类的结构,它继承自EventSource.Base

通过上面的类图,我们可以从方法和参数名中简单的看书,这个类的主要功能,其中open,next,close是每个源都需要实现的方法,用builder对source进行构造,TailThread是个线程,我最初猜测其主要功能应该是timer功能,类似于定时检查。
我们可以简单这样理解,我们配置tail源之后,首先调用的是这个类的builder这个方法(其实,这之前还经过一系列的过程,因为与TailSource的功能无关,以后在分析)

这是builder实际调用的方法,可以看到这里会根据不同的输入参数调用不同的构造方法,我们这里首先分析图上标红的方法,这其实是以\n也就是换行来分割记录的source。
构造函数很简单,就是一些参数的初始化,主要有一个参数Cursor,这也是我们分析的重点,稍后分析。

下面看一下source的语义,开始函数open,

很简单,就是启动TailThread。

下面是截取了TailThread的run方法的主要过程,

从中我们可以看出是对cursor的管理,一个cursor实际上对应一个文件,为什么会有newCursor、rmCursor等cursor列表呢,实际上flume的TailSource的实现了tail、multitail和taildir source。所以会出现上面这些数据结构。这个run方法中首先对cursor初始化,把新产生的cursor加到newCursor列表中,把旧的,不用监控的cursor加到rmCurosr中,之后就是最关键的cursor.tailBody,它来获取数据。然后根据获取数据的情况,判断是否要停顿一定的时间(当前没有获取到新数据的时候)。

下面看一下Cursor类具体结构与功能

file:tail的文件
buf:用来读文件的缓存
raf:随机访问文件引用
in:随机访问文件的通道
lastFileMod:文件最近修改时间
lastChannelPos:通道最近pos的位置,即要读取数据的位置
lastChannelSize:通道的大小,即文件的大小

tailBody()方法通过调用readAllFromChannel()方法来读取数据,而readAllFromChannel()方法又是通过extractLines(buf)方法来按行(分隔符)分割数据并传入队列

extractLines方法,只要在数据中找到\n就会返回true,否则为false,其实源码中没有利用其返回值,所以不用关心这个方法的返回值;
readAllFromChannel只有读到数据就会返回true,否则返回false;
tailBody如果读到数据,也就是readAllFromChannel返回true,则返回true;但是当readAllFromChannel没有返回true,也就是没有读到数据,说明读到了文件尾,那么就会进行一系列的判断;
1.如果文件的长度和修改时间都没有发生变化,那么说明文件确实没有变化,正常返回false就可以了。
2.其他情况有可能就是发生了文件轮转,file rotate,如定时切日志或者文件达到指定大小自动切换日志。这个方式有很多种,需要仔细了解一下。
a.如果文件的长度没变,而最近修改时间发生变化,那么进入
raf.getFD().sync(); // Alex: not sure this helps at all...Thread.sleep(1000); // sanity interval: more data may be written

强制内存与硬盘数据和元数据同步,等待1s。
b.之后再判断通道大小发生变化没有,前面的状态有可能是因为文件发生轮转而产生竞争条件,实际上数据已经读到通道里。
c.还有可能通道读到的数据小于开始获得的通道大小,这个可能是文件轮转时对数据进行了截断,(应该清空操作导致的)

一旦发生轮转则重置raf
resetRAF(); // resetting raf to catch up new file
resetRAF主要的就是关闭目前raf引用,清空缓存区。
tailBody返回文件长度是否大于0的bool值

原创粉丝点击