Flume 1.7 源代码分析(四)从Source写数据到Channel
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方法。
public static SourceRunner forSource(Source source) {
SourceRunner runner = null;
if (source instanceof PollableSource) {
runner = new PollableSourceRunner();
((PollableSourceRunner) runner).setSource((PollableSource) source);
} else if (source instanceof EventDrivenSource) {
runner = new EventDrivenSourceRunner();
((EventDrivenSourceRunner) runner).setSource((EventDrivenSource) source);
} else {
throw new IllegalArgumentException("No known runner type for source " + source);
}
return runner;
}
能够看到source分为了2种类型,并有相应的sourceRunner(PollableSourceRunner、EventDrivenSourceRunner)。
这2种source差别在于是否须要外部的驱动去获取数据,不须要外部驱动(採用自身的事件驱动机制)的称为EventDrivenSource,须要外部驱动的称为PollableSource。
- 常见的EventDrivenSource:AvroSource、ExecSource、SpoolDirectorySource。
- 常见的PollableSource:TaildirSource、kafkaSource、JMSSource。
以EventDrivenSourceRunner为例,由MonitorRunnable调用其start方法:
public void start() {
Source source = getSource();
ChannelProcessor cp = source.getChannelProcessor();
cp.initialize();//用于初始化Interceptor
source.start();
lifecycleState = LifecycleState.START;
}
这里的ChannelProcessor是比較重要的一个类,后面会详细说。接下来调用了Source的start方法。能够对比一下之前的总体架构的图。start方法实现的就是这个部分:
5.1.2 ExecSource
以ExecSource的start方法为例:
public void start() {
executor = Executors.newSingleThreadExecutor();
runner = new ExecRunnable(shell, command, getChannelProcessor(), sourceCounter, restart, restartThrottle, logStderr, bufferCount, batchTimeout, charset);
runnerFuture = executor.submit(runner);
sourceCounter.start();
super.start();
}
主要启动了一个线程runner。初始化了一下计数器。详细实现还是要看ExecRunable类的run方法:
public void run() {
do {
timedFlushService = Executors.newSingleThreadScheduledExecutor(…);
//使用配置的參数启动Shell命令
String[] commandArgs = command.split("\\s+");
process = new ProcessBuilder(commandArgs).start();
//设置标准输入流
reader = new BufferedReader(new InputStreamReader(process.getInputStream()…));
//设置错误流
StderrReader stderrReader = new StderrReader(…);
stderrReader.start();
//启动定时任务。将eventList中数据批量写入到Channel
future = timedFlushService.scheduleWithFixedDelay(new Runnable() {
public void run() {
synchronized (eventList) {
if (!eventList.isEmpty() && timeout()) {flushEventBatch(eventList);}
}
}
},batchTimeout, batchTimeout, TimeUnit.MILLISECONDS);
//按行读取标准输出流的内容,并写入eventList
while ((line = reader.readLine()) != null) {
synchronized (eventList) {
sourceCounter.incrementEventReceivedCount();
eventList.add(EventBuilder.withBody(line.getBytes(charset)))
//超出配置的大小或者超时后,将eventList写到Channel
if (eventList.size() >= bufferCount || timeout()) {flushEventBatch(eventList);}
}
}
synchronized (eventList) {if (!eventList.isEmpty()){flushEventBatch(eventList);}}
} while (restart);//假设配置了自己主动重新启动。当Shell命令的进程结束时,自己主动重新启动命令。
}
在该方法中启动了2个reader,分别取读取标准输入流和错误流,将标准输入流中的内容写入eventList。
与此同一时候启动另外一个线程,调用flushEventBatch方法。定期将eventList中的数据写入到Channel。
private void flushEventBatch(List<Event> eventList) {
channelProcessor.processEventBatch(eventList);//假如这里异常的话。eventList还没有清空
sourceCounter.addToEventAcceptedCount(eventList.size());
eventList.clear();
lastPushToChannel = systemClock.currentTimeMillis();
}
能够看到这里调用了channelProcessor.processEventBatch()来写入Channel。
5.2 Channel部分
5.2.1 ChannelProcessor
ChannelProcessor的作用是运行所有interceptor。并将eventList中的数据,发送到各个reqChannel、optChannel。ReqChannel和optChannel是通过channelSelector来获取的。
public interface ChannelSelector extends NamedComponent, Configurable {
public void setChannels(List<Channel> channels);
public List<Channel> getRequiredChannels(Event event);
public List<Channel> getOptionalChannels(Event event);
public List<Channel> getAllChannels();//获取在当前Source中配置的所有Channel
}
假设要自己定义一个ChannelSelector,仅仅须要继承AbstractChannelSelector后,实现getRequiredChannels和getOptionalChannels就可以。
ReqChannel代表一定保证存储的Channel(失败会不断重试),optChannel代表可能存储的Channel(即失败后不重试)。
ReqChannel与optChannel的差别从代码上来看。前者在出现异常时,会在运行完回滚后往上层抛,而optChannel则仅仅运行回滚。注意到回滚操作仅仅清空putList(5.2.4节会说明),而这一层假设没有抛出异常的话,调用方(也就是上节的flushEventBatch)会清空eventList,也就是异常之后的数据丢失了。
发送当中一条数据的代码例如以下:
try {
tx.begin();
reqChannel.put(event);
tx.commit();
} catch (Throwable t) {
tx.rollback();
//省略部分代码
}
当中put调用Channel的doPut方法。commit调用Channel的doCommit方法。
Channel主要包括4个主要方法:doPut、doTake、doCommit、doRollback。以下以MemoryChannel为例说明。
5.2.2 doPut方法
在这种方法中,仅仅包括了递增计数器和将事件加入到putList。
protected void doPut(Event event) throws InterruptedException {
channelCounter.incrementEventPutAttemptCount();
int eventByteSize = (int) Math.ceil(estimateEventSize(event) / byteCapacitySlotSize);
if (!putList.offer(event)) {
throw new ChannelException("");
}
putByteCounter += eventByteSize;
}
假如这种方法中出现了异常,则会抛到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的更多相关文章
- openVswitch(OVS)源代码分析之工作流程(数据包处理)
上篇分析到数据包的收发,这篇开始着手分析数据包的处理问题.在openVswitch中数据包的处理是其核心技术,该技术分为三部分来实现:第一.根据skb数据包提取相关信息封装成key值:第二.根据提取到 ...
- 【Java】【Flume】Flume-NG源代码分析的启动过程(两)
本节分析配置文件的解析,即PollingPropertiesFileConfigurationProvider.FileWatcherRunnable.run中的eventBus.post(getCo ...
- MPTCP 源码分析(四) 发送和接收数据
简述: MPTCP在发送数据方面和TCP的区别是可以从多条路径中选择一条 路径来发送数据.MPTCP在接收数据方面与TCP的区别是子路径对无序包 进行重排后,MPTCP的mpcb需要多所有子 ...
- flume【源码分析】分析Flume的拦截器
h2 { color: #fff; background-color: #7CCD7C; padding: 3px; margin: 10px 0px } h3 { color: #fff; back ...
- 新秀nginx源代码分析数据结构篇(四)红黑树ngx_rbtree_t
新秀nginx源代码分析数据结构篇(四)红黑树ngx_rbtree_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csd ...
- 【Java】【Flume】Flume-NG启动过程源代码分析(一)
从bin/flume 这个shell脚本能够看到Flume的起始于org.apache.flume.node.Application类,这是flume的main函数所在. main方法首先会先解析sh ...
- Hadoop源代码分析
http://wenku.baidu.com/link?url=R-QoZXhc918qoO0BX6eXI9_uPU75whF62vFFUBIR-7c5XAYUVxDRX5Rs6QZR9hrBnUdM ...
- Android应用程序框架层和系统运行库层日志系统源代码分析
文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6598703 在开发Android应用程序时,少 ...
- Parrot源代码分析之海贼王
我们的目的是找到speedup-example在使用Parrot加速的原因,假设仅仅说它源于Context Switch的降低,有点简单了,它究竟为什么降低了?除了Context Switch外是否还 ...
随机推荐
- 服务器操作系统应该选择 Debian/Ubuntu 还是 CentOS?
早期,我们使用 Debian 作为服务器软件,后来转向了CentOS,主要原因如下: 1.CentOS/RHEL的生命周期是7年,基本上可以覆盖硬件的生命周期,也就意味着一个新硬件安装以后,不用再次安 ...
- Softmax vs. Softmax-Loss VS cross-entropy损失函数 Numerical Stability(转载)
http://freemind.pluskid.org/machine-learning/softmax-vs-softmax-loss-numerical-stability/ 卷积神经网络系列之s ...
- 001servlet的基本知识
servlet的知识: l 1. servlet概念及相关接口简介 l 2. servet 执行过程 l 3. servlet路径映射 l 4. 缺省servlet --应用 ...
- C#三种模拟自动登录和提交POST信息的实现方法【转】
网页自动登录(提交Post内容)的用途很多,如验证身份.程序升级.网络投票等,以下是用C#实现的方法. 网页自动登录和提交POST信息的核心就是分析网页的源代码(HTML),在C#中,可以 ...
- web 汇率
http://www.cnblogs.com/beimeng/p/3789940.html 网站虽小,五脏俱全(干货) 前言 最近一个朋友让帮忙做一个汇率换算的网站,用业余时间,到最后总算是实现了 ...
- 利用flume+kafka+storm+mysql构建大数据实时系统
架构图
- Windows远程访问OEM乱码解决
问题描述 发现用Windows访问Linux安装的Oracle时oem按钮总是乱码,整理解决方法如下: OEM简介及按钮乱码问题 http://www.linuxidc.com/Linux/2013 ...
- springmvc传递有特殊字符的路径参数
因为hostKey这里是IP(例如127.0.0.1)包含了特殊字符. 实际传递到后台的是127.0.0少了一截 @GetMapping("/metrics/jobId/{jobId}/{ ...
- Linux网卡命名enp3s0说明
用了很多年Linux的我在升级Ubuntu 16.04之后竟然发现我的以太网卡的名字竟然不是eth0,变成了enp3s0,每次想要修改什么配置,都要先ifconfig查一下网卡名,真是让我很郁闷! 去 ...
- poj_2823 线段树
题目大意 给定一行数,共N个.有一个长度为K的窗口从左向右滑动,窗口中始终有K个数字,窗口每次滑动一个数字.求各个时刻窗口中的最大值和最小值. 题目分析 直接搜索,复杂度为O(n^2).本题可以看做是 ...