Flume快速入门(三):File Channel之写Event



       有了前两篇博文的基础,相信大家对Flume Agent的内部结构已经有了个初步的了解,现在我们来详细介绍最常用的文件通道——File Channel,本篇博客主要介绍Eevnt是如何完成写到File Channel这一操作的。

       上一篇: http://manzhizhen.iteye.com/blog/2298159

       Channel是联系Source和Sink的桥梁,内存Memory Channel性能虽高,但对于日志数据处理这块,实时并不是第一重要的,几乎所有以日志作为数据源的数据分析都只能说是近乎实时的。对于大多数数据分析来说,日志丢失是不可忍受的,所以,现在线上使用最多的Channel,就是File Channel。在大多数系统的设计中,为了保证高吞吐量,都会允许一小部分数据损失(比如每隔几秒再进行刷盘操作,将缓冲区的数据写如磁盘文件),但File Channel没有这样设计,它通过在一次事务中提交多个Event来提高吞吐量,做到了只要事务被提交,那么数据就不会有丢失。但需要注意,Flume Channel是没有数据副本的,意味着如果磁盘损坏,那么数据就无法恢复了。

 

我们先来看看File Channel有哪些属性可以配置:


我们在来看看,在Flume Agent的配置文件中,File Channel是怎么配置的,见下图:

 

上图是官网中的截图,可以看到,图中设置了两个目录,第一个是检查点的目录(checkpointDir),第二个是数据的目录(dataDirs)。。那么,检查点是做什么用的?我们知道,File Channel中,有一个内存队列来保存已被Source写入但还未被Sink消费的Event数据的指针,Event指针指向的就是Event在数据目录下数据文件中存放位置,所以,你很自然的就能想到检查点指的就是内存队列在某一稳定时刻的“快照”,而且每隔一段时间(checkpointIntervalFile Channel会将内存队列持久化到磁盘文件,也就是我们配置的检查点目录下。为了保证内存队列“快照”的完整性,再将内存队列持久到磁盘文件时需要锁定内存队列,就是说此过程不Source不能写ChannelSink也不能读Channel你没猜错,上面的backupCheckpointDir就是检查点目录的备份目录,因为检查点文件是经常读写的,很容易在Flume Crash时导致文件损坏,所以如果要做到快速恢复,就可以给检查点配置一个复本。

 

从GitHub上下载Flume的源码,直接定位到flume-ng-channels模块,在该模块中,你肯定直接找到的是FileChannel.java类,在FileChannel中,我们看到了如下常见属性(省略了部分其他属性):

private Integer capacity = 0;
private int keepAlive;
protected Integer transactionCapacity = 0;
private Long checkpointInterval = 0L;
private long maxFileSize;
private long minimumRequiredSpace;
private File checkpointDir;
private File backupCheckpointDir;
private File[] dataDirs;
private Log log;
private final ThreadLocal<FileBackedTransaction> transactions =
    new ThreadLocal<FileBackedTransaction>();
private boolean fsyncPerTransaction;
private int fsyncInterval;

我接着看了下FileChannel的方法,发现大部分操作的实现是在上面的log属性中,于是,我点开了Log.java。用Eclipse的Ctrl+O的快捷键,我们直接预览Log类中的内部方法和属性,发现我们想要的都在里面,将Source将Event放入Channel肯定调用的是Log#put(long transactionID, Event event)方法,Sink从Channel取Event肯定调用的是Log#get(FlumeEventPointer pointer)方法等。如果直接看Log的这些方法,是不容易看明白它是怎么被调用,是被谁调用这些问题的,于是我们先直接从Source的角度来看Channel的Event写入过程。

       每个Source都需要设置一个通道处理器(ChannelProcessor),写入Channel不是由Source来完成的,通道处理器用于暴露服务来将Event写入Channel,它是单线程的(Executors.newSingleThreadExecutor)。通道处理器写入Event的put操作有两种:processEvent和processEventBatch,分别用来写入单个Event和一次性批量写入多个Event,两个方法的内部处理过程都差不多。通道处理器将一个Event写入到Channel的整个过程包含5部分:1.将Event进行拦截器链进行过滤;2.通过通道选择器来选择该Event需要写入的哪些Channel;3.从Channel中获取一个事务;4.调用Channel的put方法将Event写入Channel;5.提交事务或回滚。其中如果某(批次)Event需要写入多个Channel,则步骤3-5是在for循环中执行的,可见,如果要将Event写入n个通道,则整个过程将产生n次事务操作。所以,纵观Source、Channel和Sink的实现,Channel的实现无疑是最重量级的。

        咱们先看第1步拦截器构造工厂(InterceptorBuilderFactory)将用户配置的拦截器组装成拦截器链,通道处理器将需要写入Channel的Event先放入拦截器链进行过滤,如果最后返回的Event不为空,说明没被过滤掉,拦截器不一定只是为了过滤Event,它还可以给Event的头部添加一些必要信息,比如数据的日志文件来源等。

       第2步是确定该(批次)Event需要写入哪些Channel,这些Channel包括要求(required)的和可选(optional)的,要求的Channel是需要保证写入成功的(如果失败则会重试),可选的Channel只会尝试写入一次,不管失败与否。每个通道处理器实例都配置有一个通道选择器(ChannelSelector),通道处理器的构造器的入参就是通道选择器,Channel的选择就是由通道选择器来做的。通道选择器会对Event的头部信息来进行筛选,决定该Event需要写入到哪些Channel,具体配置可以参考官方文档,该部分不看代码也能明白其实现,所以通道选择器不做具体介绍了。

         知道要写入哪些Channel后,通道抽利器将for循环遍历Channel列表将该(批次)Event依次放入Channel中,将该(批次)Event写入该Channel前,会先通过改Channel创建事务(步骤3),全部写入成功(步骤4)后将提交事务(步骤5,commit),否则会回滚(步骤5,rollback)并抛出异常。这将会有个潜在的问题,如果一个Event需要写入多个Channel,当写入其中某个Channel导致事务回滚时,由于在他之前的Channel已经写入成功了,所以当该(批次)Event再次被交给通道处理器时,上次那些已经成功写入Event的Channel将会被重复写入,这似乎是Flume设计的一个缺陷,所以只能下游的系统自己来处理重复消息了。写完要求(required)的Channel,将会继续将Event写入可选(optional)的Channel(如果配置了的话),写入可选的Event也是会开启事务的,但如果出错只会回滚但不会对外抛出异常。

        咱们接着看第3步

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值