org.apache.flume.source.SpoolDirectorySource是flume的一个常用的source,这个源支持从
磁盘
中某
文件夹
获取文件数据。不同于其他异步源,这个源能够避免重启或者发送失败后数据丢失。flume可以
监控
文件夹,当出现新文件时会读取该文件并获取数据。当一个给定的文件被全部读入到通道中时,该文件会被重命名以标志已经完成。同时,该源需要一个清理进程来定期移除完成的文件。
通道可选地将一个完成路径的原始文件插入到每个事件的hearder域中。在读取文件时,source缓存文件数据到内存中。同时,需要确定设置了bufferMaxLineLength选项,以确保该数据远大于输入数据中数据最长的某一行。
注意!!!channel只接收spooling directory中唯一命名的文件。如果文件名重复或文件在读取过程中被修改,则会有读取失败返回异常信息。这种场景下,同名的文件复制到这个目录时建议带唯一标示,比如时间戳。
一、configure(Context context)方法。代码如下:
1、spoolDirectory是 监控 目录,不能为空,没有默认值。这个source不具有监控子目录的功能,也就是不能递归监控。如果需要,这需要自己去实现, Flume-ng-1.3.0 spooling source的方式增加了对目录的递归检测的支持 这里有递归 检测 的实现;
2、completedSuffix是文件读取完毕后给完成文件添加的标记后缀,默认是".COMPLETED";
3、deletePolicy这是是否删除读取完毕的文件,默认是"never",就是不删除,目前只支持"never"和“IMMEDIATE”;
4、fileHeader是否在event的Header中添加文件名,boolean类型
5、fileHeaderKey这是event的Header中的key,value是文件名
6、batchSize这个是一次处理的记录数,默认是100;
7、inputCharset编码方式,默认是"UTF-8";
8、ignorePattern忽略符合条件的文件名
9、trackerDirPath被处理文件元数据的存储目录,默认".flumespool"
10、deserializerType将文件中的数据序列化成event的方式,默认是“LINE”---org.apache.flume.serialization.LineDeserializer
11、deserializerContext这个主要用在Deserializer中设置编码方式outputCharset和文件每行最大长度maxLineLength。
二、start()方法。代码如下:
1、构建了一个org.apache.flume.client.avro.ReliableSpoolingFileEventReader的对象reader;
2、启动了一个每隔POLL_DELAY_MS(默认500,单位ms)执行一次SpoolDirectoryRunnable的进程;
三、读取并发送event进程。代码如下:
该进程实现了批量读取reader所指向的文件的数据,并发送到channel。
四、org.apache.flume.client.avro.ReliableSpoolingFileEventReader的构造方法首先是先尝试对spoolDirectory是否有创建文件、读、写、删除等权限;然后在构造"$spoolDirectory/.flumespool/.flumespool-main.meta"元数据文件
五、上面SpoolDirectoryRunnable.run方法中的List<Event> events = reader.readEvents(batchSize),是org.apache.flume.client.avro.ReliableSpoolingFileEventReader.readEvents(batchSize):
1,committed初始化时是true,所以第一次运行就是通过getNextFile()获取当前要去读的文件。如果是空就返回空值了。
2,使用deserializer(默认是org.apache.flume.serialization.LineDeserializer)的readEvents(numEvents)去批量读数据封装成event。
3,如获取的批量events为空,说明这个文件读完了,需要对这个读完的文件做个“删除”(retireCurrentFile()方法,在这也会删除元数据文件),就是根据deletePolicy(删除还是添加去读完毕后缀completedSuffix);但是这个本方法是有返回值的就是events,所以需要获取下一个文件,即再次运行getNextFile(),并events = currentFile.get().getDeserializer().readEvents(numEvents)
4,是否要对这些events的Header中添加文件名
5,committed = false; lastFileRead = currentFile; 并返回events。
这个方法还有几点需要解释:
其一、就是committed参数,此参数关系到这一批量的event是否已经正确处理完毕。可以看到上面的5中所讲,每调用一次ReliableSpoolingFileEventReader.readEvents(batchSize)均会在最后将committed设置为false,但是在SpoolDirectoryRunnable.run()方法中也可以看出在调用readEvents方法后还会调用ReliableSpoolingFileEventReader.commit()方法,代码如下:
这个方法说明满足两个条件就可以:一、向trackerFile写入读到的记录位置,mark()方法会将syncPosition写入trackerFile,而ResettableFileInputStream中的position用来暂存位置增加的,待到何时会syncPosition=position,这样是为了防止出现异常时用于恢复丢失的数据;二、将committed = true。两个条件:一个是committed=false,这个执行完readEvents最后会置为false;二、currentFile“非空”,代表有正在读的文件。如果committed在readEvents中开始时为false,说明:一、event提交到channel时出现了问题,没有执行reader.commit;二、currentFile已经“为空”,说明没有可以读的文件。这两点也体现在readEvents开始部分,committed=false时,如果没有可读文件就会抛出异常File should not roll when commit is outstanding.";如果是在提交到channel时出问题会通过currentFile.get().getDeserializer().reset()重新撤回到上次正确提交channel的位置,这样可以使得不丢失数据。
其二、就是getNextFile()方法。这个方法会首先过滤 检测 目录的子目录(也就是不能递归)、隐藏文件(以"."开头的文件)、已经读完的文件(有completedSuffix后缀的)、符合ignorePattern的文件;然后将过滤后的文件按时间的先后顺序排序,再创建一个新的对应的元数据文件;构造一个读取文件的输入流ResettableFileInputStream,并将此输入流作为参数传递给deserializer,最终返回一个Optional.of(new FileInfo(nextFile, deserializer));
其三、就是LineDeserializer)的readEvents(numEvents)方法。这个方法会多次(numEvents)调用LineDeserializer(默认)的readLine()获取一行数据封装成event。readLine()会通过org.apache.flume.serialization.ResettableFileInputStream.readChar()不断的去获取数据,读完正行后判断每行的长度是否超过规定值maxLineLength。readChar()方法除了不断读取一个字符外,还会记下字符的位置,等待将位置写入元数据文件中(通过deserializer.mark()写入)
通道可选地将一个完成路径的原始文件插入到每个事件的hearder域中。在读取文件时,source缓存文件数据到内存中。同时,需要确定设置了bufferMaxLineLength选项,以确保该数据远大于输入数据中数据最长的某一行。
注意!!!channel只接收spooling directory中唯一命名的文件。如果文件名重复或文件在读取过程中被修改,则会有读取失败返回异常信息。这种场景下,同名的文件复制到这个目录时建议带唯一标示,比如时间戳。
一、configure(Context context)方法。代码如下:
1、spoolDirectory是 监控 目录,不能为空,没有默认值。这个source不具有监控子目录的功能,也就是不能递归监控。如果需要,这需要自己去实现, Flume-ng-1.3.0 spooling source的方式增加了对目录的递归检测的支持 这里有递归 检测 的实现;
2、completedSuffix是文件读取完毕后给完成文件添加的标记后缀,默认是".COMPLETED";
3、deletePolicy这是是否删除读取完毕的文件,默认是"never",就是不删除,目前只支持"never"和“IMMEDIATE”;
4、fileHeader是否在event的Header中添加文件名,boolean类型
5、fileHeaderKey这是event的Header中的key,value是文件名
6、batchSize这个是一次处理的记录数,默认是100;
7、inputCharset编码方式,默认是"UTF-8";
8、ignorePattern忽略符合条件的文件名
9、trackerDirPath被处理文件元数据的存储目录,默认".flumespool"
10、deserializerType将文件中的数据序列化成event的方式,默认是“LINE”---org.apache.flume.serialization.LineDeserializer
11、deserializerContext这个主要用在Deserializer中设置编码方式outputCharset和文件每行最大长度maxLineLength。
二、start()方法。代码如下:
1、构建了一个org.apache.flume.client.avro.ReliableSpoolingFileEventReader的对象reader;
2、启动了一个每隔POLL_DELAY_MS(默认500,单位ms)执行一次SpoolDirectoryRunnable的进程;
三、读取并发送event进程。代码如下:
该进程实现了批量读取reader所指向的文件的数据,并发送到channel。
四、org.apache.flume.client.avro.ReliableSpoolingFileEventReader的构造方法首先是先尝试对spoolDirectory是否有创建文件、读、写、删除等权限;然后在构造"$spoolDirectory/.flumespool/.flumespool-main.meta"元数据文件
五、上面SpoolDirectoryRunnable.run方法中的List<Event> events = reader.readEvents(batchSize),是org.apache.flume.client.avro.ReliableSpoolingFileEventReader.readEvents(batchSize):
1,committed初始化时是true,所以第一次运行就是通过getNextFile()获取当前要去读的文件。如果是空就返回空值了。
2,使用deserializer(默认是org.apache.flume.serialization.LineDeserializer)的readEvents(numEvents)去批量读数据封装成event。
3,如获取的批量events为空,说明这个文件读完了,需要对这个读完的文件做个“删除”(retireCurrentFile()方法,在这也会删除元数据文件),就是根据deletePolicy(删除还是添加去读完毕后缀completedSuffix);但是这个本方法是有返回值的就是events,所以需要获取下一个文件,即再次运行getNextFile(),并events = currentFile.get().getDeserializer().readEvents(numEvents)
4,是否要对这些events的Header中添加文件名
5,committed = false; lastFileRead = currentFile; 并返回events。
这个方法还有几点需要解释:
其一、就是committed参数,此参数关系到这一批量的event是否已经正确处理完毕。可以看到上面的5中所讲,每调用一次ReliableSpoolingFileEventReader.readEvents(batchSize)均会在最后将committed设置为false,但是在SpoolDirectoryRunnable.run()方法中也可以看出在调用readEvents方法后还会调用ReliableSpoolingFileEventReader.commit()方法,代码如下:
这个方法说明满足两个条件就可以:一、向trackerFile写入读到的记录位置,mark()方法会将syncPosition写入trackerFile,而ResettableFileInputStream中的position用来暂存位置增加的,待到何时会syncPosition=position,这样是为了防止出现异常时用于恢复丢失的数据;二、将committed = true。两个条件:一个是committed=false,这个执行完readEvents最后会置为false;二、currentFile“非空”,代表有正在读的文件。如果committed在readEvents中开始时为false,说明:一、event提交到channel时出现了问题,没有执行reader.commit;二、currentFile已经“为空”,说明没有可以读的文件。这两点也体现在readEvents开始部分,committed=false时,如果没有可读文件就会抛出异常File should not roll when commit is outstanding.";如果是在提交到channel时出问题会通过currentFile.get().getDeserializer().reset()重新撤回到上次正确提交channel的位置,这样可以使得不丢失数据。
其二、就是getNextFile()方法。这个方法会首先过滤 检测 目录的子目录(也就是不能递归)、隐藏文件(以"."开头的文件)、已经读完的文件(有completedSuffix后缀的)、符合ignorePattern的文件;然后将过滤后的文件按时间的先后顺序排序,再创建一个新的对应的元数据文件;构造一个读取文件的输入流ResettableFileInputStream,并将此输入流作为参数传递给deserializer,最终返回一个Optional.of(new FileInfo(nextFile, deserializer));
其三、就是LineDeserializer)的readEvents(numEvents)方法。这个方法会多次(numEvents)调用LineDeserializer(默认)的readLine()获取一行数据封装成event。readLine()会通过org.apache.flume.serialization.ResettableFileInputStream.readChar()不断的去获取数据,读完正行后判断每行的长度是否超过规定值maxLineLength。readChar()方法除了不断读取一个字符外,还会记下字符的位置,等待将位置写入元数据文件中(通过deserializer.mark()写入)