Flume 1.7 源代码分析(一)源代码编译

Flume 1.7 源代码分析(二)总体架构

Flume 1.7 源代码分析(三)程序入口

Flume 1.7 源代码分析(四)从Source写数据到Channel

Flume 1.7 源代码分析(五)从Channel获取数据写入Sink

5 从Source写数据到Channel

5.1 Source部分

5.1.1 SourceRunner

SourceRunner就是专门用于运行Source的一个类。

在”物化配置”一节获取配置信息后,会依据Source去获取详细的SourceRunner,调用的是SourceRunner的forSource方法。

  1. public static SourceRunner forSource(Source source) {
  2. SourceRunner runner = null;
  3. if (source instanceof PollableSource) {
  4. runner = new PollableSourceRunner();
  5. ((PollableSourceRunner) runner).setSource((PollableSource) source);
  6. } else if (source instanceof EventDrivenSource) {
  7. runner = new EventDrivenSourceRunner();
  8. ((EventDrivenSourceRunner) runner).setSource((EventDrivenSource) source);
  9. } else {
  10. throw new IllegalArgumentException("No known runner type for source " + source);
  11. }
  12. return runner;
  13. }

能够看到source分为了2种类型,并有相应的sourceRunner(PollableSourceRunner、EventDrivenSourceRunner)。

这2种source差别在于是否须要外部的驱动去获取数据,不须要外部驱动(採用自身的事件驱动机制)的称为EventDrivenSource,须要外部驱动的称为PollableSource。

  • 常见的EventDrivenSource:AvroSource、ExecSource、SpoolDirectorySource。
  • 常见的PollableSource:TaildirSource、kafkaSource、JMSSource。

以EventDrivenSourceRunner为例,由MonitorRunnable调用其start方法:

  1. public void start() {
  2. Source source = getSource();
  3. ChannelProcessor cp = source.getChannelProcessor();
  4. cp.initialize();//用于初始化Interceptor
  5. source.start();
  6. lifecycleState = LifecycleState.START;
  7. }

这里的ChannelProcessor是比較重要的一个类,后面会详细说。接下来调用了Source的start方法。能够对比一下之前的总体架构的图。start方法实现的就是这个部分:

5.1.2 ExecSource

以ExecSource的start方法为例:

  1. public void start() {
  2. executor = Executors.newSingleThreadExecutor();
  3. runner = new ExecRunnable(shell, command, getChannelProcessor(), sourceCounter, restart, restartThrottle, logStderr, bufferCount, batchTimeout, charset);
  4. runnerFuture = executor.submit(runner);
  5. sourceCounter.start();
  6. super.start();
  7. }

主要启动了一个线程runner。初始化了一下计数器。详细实现还是要看ExecRunable类的run方法:

  1. public void run() {
  2. do {
  3. timedFlushService = Executors.newSingleThreadScheduledExecutor(…);
  4. //使用配置的參数启动Shell命令
  5. String[] commandArgs = command.split("\\s+");
  6. process = new ProcessBuilder(commandArgs).start();
  7. //设置标准输入流
  8. reader = new BufferedReader(new InputStreamReader(process.getInputStream()…));
  9. //设置错误流
  10. StderrReader stderrReader = new StderrReader(…);
  11. stderrReader.start();
  12. //启动定时任务。将eventList中数据批量写入到Channel
  13. future = timedFlushService.scheduleWithFixedDelay(new Runnable() {
  14. public void run() {
  15. synchronized (eventList) {
  16. if (!eventList.isEmpty() && timeout()) {flushEventBatch(eventList);}
  17. }
  18. }
  19. },batchTimeout, batchTimeout, TimeUnit.MILLISECONDS);
  20. //按行读取标准输出流的内容,并写入eventList
  21. while ((line = reader.readLine()) != null) {
  22. synchronized (eventList) {
  23. sourceCounter.incrementEventReceivedCount();
  24. eventList.add(EventBuilder.withBody(line.getBytes(charset)))
  25. //超出配置的大小或者超时后,将eventList写到Channel
  26. if (eventList.size() >= bufferCount || timeout()) {flushEventBatch(eventList);}
  27. }
  28. }
  29. synchronized (eventList) {if (!eventList.isEmpty()){flushEventBatch(eventList);}}
  30. } while (restart);//假设配置了自己主动重新启动。当Shell命令的进程结束时,自己主动重新启动命令。
  31. }

在该方法中启动了2个reader,分别取读取标准输入流和错误流,将标准输入流中的内容写入eventList。

与此同一时候启动另外一个线程,调用flushEventBatch方法。定期将eventList中的数据写入到Channel。

  1. private void flushEventBatch(List<Event> eventList) {
  2. channelProcessor.processEventBatch(eventList);//假如这里异常的话。eventList还没有清空
  3. sourceCounter.addToEventAcceptedCount(eventList.size());
  4. eventList.clear();
  5. lastPushToChannel = systemClock.currentTimeMillis();
  6. }

能够看到这里调用了channelProcessor.processEventBatch()来写入Channel。

5.2 Channel部分

5.2.1 ChannelProcessor

ChannelProcessor的作用是运行所有interceptor。并将eventList中的数据,发送到各个reqChannel、optChannel。ReqChannel和optChannel是通过channelSelector来获取的。

  1. public interface ChannelSelector extends NamedComponent, Configurable {
  2. public void setChannels(List<Channel> channels);
  3. public List<Channel> getRequiredChannels(Event event);
  4. public List<Channel> getOptionalChannels(Event event);
  5. public List<Channel> getAllChannels();//获取在当前Source中配置的所有Channel
  6. }

假设要自己定义一个ChannelSelector,仅仅须要继承AbstractChannelSelector后,实现getRequiredChannels和getOptionalChannels就可以。

ReqChannel代表一定保证存储的Channel(失败会不断重试),optChannel代表可能存储的Channel(即失败后不重试)。

ReqChannel与optChannel的差别从代码上来看。前者在出现异常时,会在运行完回滚后往上层抛,而optChannel则仅仅运行回滚。注意到回滚操作仅仅清空putList(5.2.4节会说明),而这一层假设没有抛出异常的话,调用方(也就是上节的flushEventBatch)会清空eventList,也就是异常之后的数据丢失了。

发送当中一条数据的代码例如以下:

  1. try {
  2. tx.begin();
  3. reqChannel.put(event);
  4. tx.commit();
  5. } catch (Throwable t) {
  6. tx.rollback();
  7. //省略部分代码
  8. }

当中put调用Channel的doPut方法。commit调用Channel的doCommit方法。

Channel主要包括4个主要方法:doPut、doTake、doCommit、doRollback。以下以MemoryChannel为例说明。

5.2.2 doPut方法

在这种方法中,仅仅包括了递增计数器和将事件加入到putList。

  1. protected void doPut(Event event) throws InterruptedException {
  2. channelCounter.incrementEventPutAttemptCount();
  3. int eventByteSize = (int) Math.ceil(estimateEventSize(event) / byteCapacitySlotSize);
  4. if (!putList.offer(event)) {
  5. throw new ChannelException("");
  6. }
  7. putByteCounter += eventByteSize;
  8. }

假如这种方法中出现了异常,则会抛到ChannelProcessor中运行回滚操作。

5.2.3 doCommit方法

这种方法是比較复杂的方法之中的一个。原因在于put和take操作的commit都是通过这种方法来进行的,所以代码里面事实上混合了2个功能(即put和take操作)所需的提交代码。

单纯从Source写数据到Channel这件事情,流程为eventList->putList->queue。

因为前面已经完毕了把数据放到putList中。那接下来要做的事情就是将putList中数据放入queue中就能够了。

这个部分先说明到这里,下一个章节结合take操作一起看这种方法。

5.2.4 doRollback方法

与doCommit方法相似,这里的回滚,也分为2种情况:由take操作引起的和由put方法引起的。

这里先说由put发起的,该transaction的流程例如以下:

eventList->putList->queue

因为doPut和doCommit运行出现异常就直接跳出了,还没运行清空语句(这里能够參考“ExecSource“章节的最后一段代码的凝视部分),也就是eventList还没有清空。所以能够直接清空putList。这样下次循环还会又一次读取该eventList中的数据。

附注:在put操作commit的时候,假设部分数据已经放进queue的话,这个时候回滚,那是否存在数据反复问题呢?依据代码。因为在放队列这个操作之前已经做过非常多推断(容量等等),这个操作仅仅是取出放进队列的操作,而这个代码之后。也仅仅是一些设置计数器的操作,理论上不会出现异常导致回滚了。

Flume 1.7 源代码分析(四)从Source写数据到Channel的更多相关文章

  1. openVswitch(OVS)源代码分析之工作流程(数据包处理)

    上篇分析到数据包的收发,这篇开始着手分析数据包的处理问题.在openVswitch中数据包的处理是其核心技术,该技术分为三部分来实现:第一.根据skb数据包提取相关信息封装成key值:第二.根据提取到 ...

  2. 【Java】【Flume】Flume-NG源代码分析的启动过程(两)

    本节分析配置文件的解析,即PollingPropertiesFileConfigurationProvider.FileWatcherRunnable.run中的eventBus.post(getCo ...

  3. MPTCP 源码分析(四) 发送和接收数据

    简述:      MPTCP在发送数据方面和TCP的区别是可以从多条路径中选择一条 路径来发送数据.MPTCP在接收数据方面与TCP的区别是子路径对无序包 进行重排后,MPTCP的mpcb需要多所有子 ...

  4. flume【源码分析】分析Flume的拦截器

    h2 { color: #fff; background-color: #7CCD7C; padding: 3px; margin: 10px 0px } h3 { color: #fff; back ...

  5. 新秀nginx源代码分析数据结构篇(四)红黑树ngx_rbtree_t

    新秀nginx源代码分析数据结构篇(四)红黑树ngx_rbtree_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csd ...

  6. 【Java】【Flume】Flume-NG启动过程源代码分析(一)

    从bin/flume 这个shell脚本能够看到Flume的起始于org.apache.flume.node.Application类,这是flume的main函数所在. main方法首先会先解析sh ...

  7. Hadoop源代码分析

    http://wenku.baidu.com/link?url=R-QoZXhc918qoO0BX6eXI9_uPU75whF62vFFUBIR-7c5XAYUVxDRX5Rs6QZR9hrBnUdM ...

  8. Android应用程序框架层和系统运行库层日志系统源代码分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6598703 在开发Android应用程序时,少 ...

  9. Parrot源代码分析之海贼王

    我们的目的是找到speedup-example在使用Parrot加速的原因,假设仅仅说它源于Context Switch的降低,有点简单了,它究竟为什么降低了?除了Context Switch外是否还 ...

随机推荐

  1. C++ operator关键字(重载操作符)

    operator是C++的关键字,它和运算符一起使用,表示一个运算符函数,理解时应将operator=整体上视为一个函数名.     这是C++扩展运算符功能的方法,虽然样子古怪,但也可以理解:一方面 ...

  2. asp.net 正在加载效果实现

    最近研究了下asp.net 正在加载的实现原理,总结了以下实现方法 首先,我们有个div显示内容为正在加载..   当然也可以考虑用图片或者其他的,不过考虑到速度,建议直接文字提示就行,然后设置div ...

  3. Linux下,PHP扩展安装(使用yum安装)

    直接操作linux,在命令模式下用yum 来安装PHP的扩展: 扩展:mysqli 命令: yum install php-mysqli 扩展:pdo 命令: yum install php-pdo

  4. KMP算法完整教程 (下)

    下面我们用数学归纳法来解决这个填值的问题. 这里我们借鉴数学归纳法的三个步骤(或者说是动态规划?): 1.初始状态 2.假设第j位以及第j位之前的我们都填完了 3.推论第j+1位该怎么填 初始状态我们 ...

  5. 在Swing的组件中,基本上都是在AWT组件的名称前面加“J”

    在Swing的组件中,基本上都是在AWT组件的名称前面加“J”. 一般情况下,除了Choise等组件: import javax.swing.*;好要加上:import java.awt.*以及imp ...

  6. Struts2的拦截器是如何使用AOP工作的

    拦截器(interceptor)是Struts2最强大的特性之一,也可以说是struts2的核心,拦截器可以让你在Action和result被执行之前或之后进行一些处理.同时,拦截器也可以让你将通用的 ...

  7. java--Struts中请求的过程

    一个请求在Struts2框架中的处理步骤: a) 客户端初始化一个指向Servlet容器的请求: b) 根据Web.xml配置,请求首先经过ActionContextCleanUp过滤器,其为可选过滤 ...

  8. php -- php数组相关函数

    array range ( mixed $low , mixed $high [, number $step ] ) 创建一个连续的数组 range('a','z'); foreach (range( ...

  9. 【BZOJ】1058: [ZJOI2007]报表统计(splay+set)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1058 当复习一下splay.... 做法很简单..... 观察得知每一次插入一个点只需要维护前后的绝 ...

  10. 《Programming with Objective-C》第七章 Values and Collections

    1.平台相关的数据类型 These types, like NSInteger and NSUInteger, are defined differently depending on the tar ...