org.apache.flume.channel.MemoryChannel类是Flume-NG的memory-channel。

private LinkedBlockingDeque<Event> queue;//mem-channel存放数据的地方

private Semaphore queueRemaining;//queue存量信号量

private Semaphore queueStored;//queue存量的信号量,保证channel里面有数据

private Semaphore bytesRemaining;//剩余字节信号量,以100字节为一个单位,也是动态调整的

Flume-NG的组件(source、sink、channel)总是先通过configure(Context context)方法,获取配置文件中的配置信息,在这配置了mem的最大容量capacity、事务的event最大容量transCapacity、缓存百分比byteCapacityBufferPercentage、最大内存所有事件允许总字节数      byteCapacity。还有信号量的初始化:

synchronized(queueLock) {//初始化mem

queue = new LinkedBlockingDeque<Event>(capacity);

queueRemaining = new Semaphore(capacity);

queueStored = new Semaphore(0);

}

以及:bytesRemaining = new Semaphore(byteCapacity);

queueStored这个比较特殊,初始为0表示开始时,queue没有数据,只要queue的大小有所调整时就需要调整这个信号量,增加就release,减少就tryAcquire。

当然在configure方法中可以看到如果配置文件修改后是如何动态修改的(flume默认每30s扫描加载一次配置文件)。

然后start()方法进行一些初始化操作。

resizeQueue(capacity)方法用来动态加载配置文件,调整mem容量的。

createTransaction()方法,返回MemoryTransaction实例。

estimateEventSize(Event event)方法,返回event.body的字节长度。

该类有一个内部类MemoryTransaction是mem-channel从source取(put)数据、给(take)sink的操作类。其初始化时会创建两个LinkedBlockingDeque,一个是takeList用于sink的take;一个是putList用于source的put,两个队列的容量都是事务的event最大容量transCapacity。两个队列是用于事务回滚rollback和提交commit的。

Source交给channel处理的一般是调用ChannelProcessor类的processEventBatch(List<Event> events)方法或者processEvent(Event event)方法;在sink端可以直接使用channel.take()方法获取其中的一条event数据。这俩方法在将event提交至channel时,都需要:

一、获取channel列表。List<Channel> requiredChannels = selector.getRequiredChannels(event);

二、通过channel获取Transaction。Transaction tx = reqChannel.getTransaction();

三、tx.begin();

四、reqChannel.put(event)(在sink中这是take(event)方法);

五、tx.commit();

六、tx.rollback();

七、tx.close()。

上面的三~七中的方法,最终调用的是MemoryTransaction的doBegin(未重写,默认空方法)、doPut、doCommit、doRollback、doClose(未重写,默认空方法)方法。

其中doPut方法,先计算event的大小可以占用bytesRemaining的多大空间,然后在有限的时间内等待获取写入空间,获取之后写入putList暂存。

doTake方法,先检查takeList的剩余容量;再检查是否有许可进行取操作(queueStored使得可以不用阻塞其它线程获取许可信息);然后同步的从queue中取一个event,再放入takeList,并返回此event。

doCommit方法,不管在sink还是source端都会调用。首先检查queue队列中是否有足够空间放得下从source过来的数据,依据就是queueRemaining是否有remainingChange = takeList.size-putList.size个许可。然后是将putList中的所有数据提交到内存队列queue之中,并将putList和takeList清空。清空表明:运行到这步说明takeList中的数据无需再保留,putList中的数据可以放入queue中。由于在doTake中从queue取数据,所以queueStored在减,但在doCommit中会把putList中的数据放入queue所以需要增加queueStored:queueStored.release(puts);bytesRemaining在doPut中获得了一些许可会减少,在doCommit中由于takeList会清空所有会增加bytesRemaining:bytesRemaining.release(takeByteCounter);而queueRemaining在doPut和doTake中并未进行操作,而且doCommit方法在sink和source中都会调用,故而在此方法中修改takeList和putList的差值即可:queueRemaining.release(remainingChange)(在此有个细节,在doCommit的开始remainingChange如果小于0,说明剩余空间不足以放入整个putList,要么超时报错退出;要么获得足够的许可,如果是后者的话就不需要再调整queueRemaining因为是在现在的基础之上减,如果remainingChange大于0,说明去除takeList大小后不仅足以放入整个putList,而且还有剩余,queueRemaining需要释放remainingChange)。其他就是修改计数器。

doRollback方法是在上面三、四、五出现异常的时候调用的,用于事务回滚。不管是在sink还是source中,都会调用。将takeList中的所有数据重新放回queue中:

while(!takeList.isEmpty()) {

queue.addFirst(takeList.removeLast());//回滚时,重新放回queue中。可能会重复(commit阶段出错,已经take的数据需要回滚,批量的情况)

}

然后清空putList:

putList.clear(); //这个方法可能发生在put中,也可能发生在take中,所以需要同步清空。可能会丢数据(还在put的阶段,没到commit阶段,出错会导致回滚,导致已经put还未放入queue中的数据会丢失)

由于putList清空了,所以bytesRemaining.release(putByteCounter);

由于takeList又返回给了queue所以queue的量增加了:queueStored.release(takes)。

  在分层的分布式flume中,一旦汇总节点中断,而采集节点使用mem,则采集会大量的丢失数据,因为channel会因为put而快速的填满,填满之后再调用put会迸发异常,致使出现异常引起事务回滚,回滚会直接清空putList,使数据丢失,只留下channel中的数据(这些数据是一开始放入进去的后来的会丢失)。putList.offer会因为填满数据返回false,add方法如果队列满了则会爆异常。

讲解并不一定完全正确,希望大伙踊跃交流。

flume-ng源码阅读memory-channel(原创)的更多相关文章

  1. Flume-NG源码阅读之SpoolDirectorySource(原创)

    org.apache.flume.source.SpoolDirectorySource是flume的一个常用的source,这个源支持从磁盘中某文件夹获取文件数据.不同于其他异步源,这个源能够避免重 ...

  2. Flume-NG源码阅读之Interceptor(原创)

    有的时候希望通过Flume将读取的文件再细分存储,比如讲source的数据按照业务类型分开存储,具体一点比如类似:将source中web.wap.media等的内容分开存储:比如丢弃或修改一些数据.这 ...

  3. Flume-NG源码阅读之SourceRunner,及选择器selector和拦截器interceptor的执行

    在AbstractConfigurationProvider类中loadSources方法会将所有的source进行封装成SourceRunner放到了Map<String, SourceRun ...

  4. Netty源码阅读之如何将TCP的读写操作和指定线程绑定

    原文链接:http://xueliang.org/article/detail/20200712234015993 前言 在Netty的线程模型中,对于一个TCP连接的读写操作,都是由一个单线程完成的 ...

  5. JDK1.8源码阅读系列之四:HashMap (原创)

    本篇随笔主要描述的是我阅读 HashMap 源码期间的对于 HashMap 的一些实现上的个人理解,用于个人备忘,有不对的地方,请指出- 接下来会从以下几个方面介绍 HashMap 源码相关知识: 1 ...

  6. [原创]chromium源码阅读-进程间通信IPC.消息的接收与应答

    chromium源码阅读-进程间通信IPC.消息的接收与应答   chromium源码阅读-进程间通信IPC.消息的接收与应答 介绍 chromium进程间通信在win32下是通过命名管道的方式实现的 ...

  7. Spark源码阅读之存储体系--存储体系概述与shuffle服务

    一.概述 根据<深入理解Spark:核心思想与源码分析>一书,结合最新的spark源代码master分支进行源码阅读,对新版本的代码加上自己的一些理解,如有错误,希望指出. 1.块管理器B ...

  8. TiDB 源码阅读系列文章(一)序

    原创: 申砾 PingCAP  2018-02-28 在 TiDB DevCon2018 上,我们对外宣布了 TiDB 源码阅读分享活动,承诺对外发布一系列文章以及视频帮助大家理解 TiDB 源码.大 ...

  9. 【Dubbo源码阅读系列】服务暴露之远程暴露

    引言 什么叫 远程暴露 ?试着想象着这么一种场景:假设我们新增了一台服务器 A,专门用于发送短信提示给指定用户.那么问题来了,我们的 Message 服务上线之后,应该如何告知调用方服务器,服务器 A ...

  10. Flume-NG源码阅读之AvroSink

    org.apache.flume.sink.AvroSink是用来通过网络来传输数据的,可以将event发送到RPC服务器(比如AvroSource),使用AvroSink和AvroSource可以组 ...

随机推荐

  1. MongoDB-1:安装和配置

    一.简介 MongoDB一种非关系型数据库(NoSql),是一种强大.灵活.可扩展的数据存储方式,因为MongoDB是文档模型,自由灵活很高,可以让你在开发过程中畅顺无比,对于大数据量.高并发.弱事务 ...

  2. Js 实现ajax

    一.JS实现的ajax 1.AJAX核心(XMLHttpRequest) 其实AJAX就是在Javascript中多添加了一个对象:XMLHttpRequest对象.所有的异步交互都是使用XMLHtt ...

  3. Vue(5)- axios、vuex

    一.内容回顾 1.webpack(前端中工作,项目上线之前对整个前端项目优化) - entry:整个项目的程序入口(main.js或index.js): - output:输出的出口: - loade ...

  4. 【零基础学习iOS开发】【02-C语言】08-基本运算

    一.算术运算符 算术运算符很地简单.就是小学数学里面的一些加减乘除操作.只是呢.还是有一些语法细节须要注意的. 1.加法运算符 + 1 int a = 10; 2 3 int b = a + 5; 在 ...

  5. oracle入门(3)——oracle服务解释

    [本文介绍] oracle不像mysql,安装后之后一个服务,如果mysql连接不上,打开其服务就行.oracle是有多个服务,哪些服务要开,哪些服务没必要开,对新手来说未必不是一个难点.下面对ora ...

  6. SignalTap II进阶学习

    1. 多级触发 有时候我们可能需要利用多个信号(最多10个)依次触发后,观察特定的值.这这时候我们可以增加触发条件来满足我们的需求. 多个触发信号如上图所示,只有依次发生SW[0]上升沿. SW[1] ...

  7. webdriver的API

    基本API 1.页面刷新    driver.fresh() 2.页面切换   driver.back().  driver.forward() 3.设置窗口大小    driver.set_wind ...

  8. gc摘要

    1. Sun JDK 1.6 GC(Garbage Collector) http://bluedavy.com2010-05-13 V0.2 2010-05-19 V0.52010-06-01 V0 ...

  9. Silverlight中获取控件中子控件

    如题:,直接来看代码: /// <summary> /// 查找并返回第一个 相同 name的子元素 /// </summary> /// <typeparam name ...

  10. J.U.C之AQS

    AQS是J.U.C的核心 AQS(AbstractQueuedSynchronizer)队列同步器,AQS是JDK下提供的一套用于实现基于FIFO等待队列的阻塞锁和相关的同步器的一个同步框架. 同步器 ...