flume spoolDirectory Source原生代码流程及其拓展

来源:互联网 发布:eclipse端口号在哪改 编辑:程序博客网 时间:2024/06/08 02:00

最近我们要在flume的基础上做定制开发。我这块主要的开发是,对zip包中的文件做一定的处理后,以avro方式序列化。整个流程参考了flume的spoolDirectorySource的实现,因为主要是对spooldirectorySource中文件解析的部分进行了参考,所以下面的文章将会着重分析文件的解析部分。


特此声明:该文仅做记录和探讨之用,文中没有涉及任何公司业务相关的部分。


1、  spoolDirectorySource简要介绍

SpoolingDirectorySource可以监控指定目录,如果有有新文件出现在指定目录,source会将这个新文件按照配置中的的序列化类型,将文件序列化成一个个event。在文件中的所有的内容都读取到Channel之后(commit),Spooling Directory Source才会对文件进行重命名或者删除,这样就保证了数据不会丢失,虽然可能有一些冗余,但是是在我们接受范围内的。


2、spoolDirectorySource工作流程


下图是根据代码流程画的一个流程图,可能会有些谬误,仅供参考哈。



2.1、 Function--configure

启动spool directory source第一步,configure方法。通过SpoolZipDirectorySourceConfigurationConstants中定义的那些配置,将context 中的相关配置信息提取出来,比如文件输入路径(SpoolZipDirectory);同时在方法里也对一些不能为空的参数族做判断,保证必须参数的存在。

public synchronized void configure(Context context) {SpoolZipDirectory= context.getString(SPOOL_ZIP_DIRECTORY);Preconditions.checkState(SpoolZipDirectory!= null, "Configuration must specify a spooling directory");completedSuffix= context.getString(SPOOLED_FILE_SUFFIX, DEFAULT_SPOOLED_FILE_SUFFIX);deletePolicy= context.getString(DELETE_POLICY, DEFAULT_DELETE_POLICY);fileHeader= context.getBoolean(FILENAME_HEADER,DEFAULT_FILE_HEADER);……}


2.2、Function--start

获取配置信息后,就可以执行start方法启动source。


2.2.1、在start方法中,首先通过ReliableSpoolingFileEventReader.java中的builder类创建了一个ReliableSpoolingFileEventReader对象。  

try{reader = new ReliableSpoolingFileEventReader.Builder()      .spoolDirectory(directory)      .completedSuffix(completedSuffix)      .includePattern(includePattern)      .ignorePattern(ignorePattern)      .trackerDirPath(trackerDirPath)...

2.2.2、再创建一个SpoolZipDirectoryRunnable对象,用定时任务启动runnable对象线程。

Runnable runner = new SpoolZipDirectoryRunnable(reader, sourceCounter);executor.scheduleWithFixedDelay(runner, 0, pollDelay, TimeUnit.MILLISECONDS);

2.3、Thread--run

在线程方法run中,执行ReliableSpoolingFileEventReader对象reader的readEvents方法。批量读取reader中获取的event(在这里,一行数据组织成一个event)。

public void run() {intbackoffInterval = 250;try {while (!Thread.interrupted()) {      List<Event> events = reader.readEvents(batchSize);if (events.isEmpty()) {break;      }......

2.4、Funtion--readEvents

在ReliableSpoolingFileEventReader.java的readEvents方法中,主要是获取currentFile,并且通过EventDeserializerFactory(事件并行转换器工厂)选择所需的并行转换类型(LINE或者AVRO或者自定义),将文件中的每行数据并行转换成event list提供读取。

2.4.1、获取当前文件(currentFile),通过getNextFile方法获取当前文件。在getNextFile方法中,主要是通过getCandidateFiles方法获取需要处理的文件列表,根据规则(读取最新/最旧/随机文件),将文件列表中的一个文件作为当前文件(currentFile)来处理。
 
2.4.2、在getCandidateFiles方法中,主要利用了java.nio.files.File.walkFileTree方法,程序不断访问目录,一旦有新文件出现在该目录下,就触发执行visitFile方法,将文件加入文件列表,并返回。

public FileVisitResultvisitFile(Path candidate, BasicFileAttributesattrs) throws IOException {    String fileName = candidate.getFileName().toString();if(!fileName.endsWith(ReliableSpoolingFileEventReader.this.completedSuffix) && !fileName.startsWith(".") &&ReliableSpoolingFileEventReader.this.includePattern.matcher(fileName).matches() && !ReliableSpoolingFileEventReader.this.ignorePattern.matcher(fileName).matches()) {            candidateFiles.add(candidate.toFile());}            return FileVisitResult.CONTINUE;}

2.5、Function--openFile

获取到当前文件(cuurentFile)之后,执行openFile方法。在openFile方法中,首先把文件作为参数,创建了ResettableFileInputStream对象in,这个对象主要保存文件的输入流以及文件读取标记。再把in和deserializerType作为参数,创建了EventDeserializer对象deserializer,deserializer根据传入的deserializerType的类型(LINE/AVRO/OTHER),将文件并行序列化为List<Event>,最终调用readEvents读取。


private Optional<ReliableSpoolingFileEventReader.FileInfo>openFile(File file) {……Preconditions.checkState(tracker.getTarget().equals(e), "Tracker target %s does not equal expected filename %s", new Object[]{tracker.getTarget(), e});ResettableFileInputStream in = new ResettableFileInputStream(file, tracker, 16384, this.inputCharset, this.decodeErrorPolicy);EventDeserializerdeserializer = EventDeserializerFactory.getInstance(this.deserializerType, this.deserializerContext, in);return Optional.of(new ReliableSpoolingFileEventReader.FileInfo(file, deserializer));……


publicenumEventDeserializerType {    LINE(Builder.class),    AVRO(org.apache.flume.serialization.AvroEventDeserializer.Builder.class),    OTHER((Class)null);private final Class<? extends org.apache.flume.serialization.EventDeserializer.Builder>builderClass;


public class LineDeserializerimplements EventDeserializer {public Event readEvent() throws IOException {this.ensureOpen();        String line = this.readLine();return line == null?null:EventBuilder.withBody(line, this.outputCharset);    }public List<Event>readEvents(intnumEvents) throws IOException {this.ensureOpen();LinkedList events = Lists.newLinkedList();for(inti = 0; i<numEvents; ++i) {            Event event = this.readEvent();if(event == null) {break; }events.add(event);        }return events;    }


3、定制开发ZipSource代码流程图

参考了上述的spoolDirectorySource的流程,我重写了一个zipSource。主要实现的功能是,将zip包中的文件,在zip流中做相应处理后,落地成avsc File, 然后再序列化成一个个event。主要的流程图如下。










原创粉丝点击