目录解读
最近接触到Flume,这里通过一些小案例做一些学习的分享。主要包括以下内容:
1-概念、2-源码编译、3-快速入门:https://www.imooc.com/article/278218
4-源码解读
5-TAILDIR监听日志文件,源码修改,新增系统名称
6-TAILDIR监听日志文件到HDFS的案例
7-TAILDIR监听日志文件到Kafka的案例
8-TAILDIR监听日志文件到ES6.X版本的案例(包括自己实现ES高版本的Sink)
注:本系列所有文章基于Flume 1.7.0
所有分析和注释代码都在:https://github.com/lizu18xz/flume-release-1.7.0
源码解读
通过概念和快速的入门,大体对flume进行了了解。下面让我们一起看一下flume的源码,
方便我们后续的使用。
(1)flume-ng-node
在这个模块下面找到org.apache.flume.node.Application入口类。就可以看到启动
的main函数:
***对我们启动命令的参数进行解析***
CommandLineParser parser = new GnuParser();
CommandLine commandLine = parser.parse(options, args);
***根据命令获取到我们的配置文件***
logger.info("flume加载的配置文件名称:"+commandLine.getOptionValue('f'));
File configurationFile = new File(commandLine.getOptionValue('f'));
***获取配置信息,启动source,channel,sink组件***
if (reload) {
logger.info("启动PollingPropertiesFileConfigurationProvider进行轮训检查配置文件是否修改!!!!!!");
EventBus eventBus = new EventBus(agentName + "-event-bus");
PollingPropertiesFileConfigurationProvider configurationProvider =
new PollingPropertiesFileConfigurationProvider(
agentName, configurationFile, eventBus, 30);
components.add(configurationProvider);
application = new Application(components);
eventBus.register(application);
} else {
PropertiesFileConfigurationProvider configurationProvider =
new PropertiesFileConfigurationProvider(agentName, configurationFile);
application = new Application();
application.handleConfigurationEvent(configurationProvider.getConfiguration());
}
***reload这种情况***
可以看到会初始化一个PollingPropertiesFileConfigurationProvider类,这个类的作用
就是当调用 application.start() 的时候 会启动一个定时的任务检查配置文件是否修改
executorService = Executors.newSingleThreadScheduledExecutor(
new ThreadFactoryBuilder().setNameFormat("conf-file-poller-%d")
.build());
FileWatcherRunnable fileWatcherRunnable =
new FileWatcherRunnable(file, counterGroup);
executorService.scheduleWithFixedDelay(fileWatcherRunnable, 0, interval,
TimeUnit.SECONDS);
可以看到FileWatcherRunnable里面的具体实现:
public void run() {
LOGGER.debug("Checking file:{} for changes", file);
counterGroup.incrementAndGet("file.checks");
long lastModified = file.lastModified();
if (lastModified > lastChange) {
LOGGER.info("Reloading configuration file:{}", file);
counterGroup.incrementAndGet("file.loads");
lastChange = lastModified;
try {
eventBus.post(getConfiguration());//会调用回调函数handleConfigurationEvent
} catch (Exception e) {
...
} catch (NoClassDefFoundError e) {
...
} catch (Throwable t) {
...
}
}
}
***eventBus.post(getConfiguration())***
getConfiguration()非常重要
它会解析我们的配置文件,获取我们配置的具体实现类
loadChannels(agentConf, channelComponentMap);
loadSources(agentConf, channelComponentMap, sourceRunnerMap);
loadSinks(agentConf, channelComponentMap, sinkRunnerMap);
比如loadChannels:
Channel channel = getOrCreateChannel(channelsNotReused,
comp.getComponentName(), comp.getType());
在ChannelType里面定义了所支持的Channel类型和具体的实现类
MEMORY("org.apache.flume.channel.MemoryChannel")
FILE("org.apache.flume.channel.file.FileChannel")
同理loadSources也差不多具体实现跨越深入此方法
Source source = sourceFactory.create(comp.getComponentName(),
comp.getType());
SourceType里面会定义支持的Source类型
TAILDIR("org.apache.flume.source.taildir.TaildirSource")
AVRO("org.apache.flume.source.AvroSource")
最后看一下loadSinks会发现也一样
//获取具体的sink实现
Sink sink = sinkFactory.create(comp.getComponentName(), comp.getType());
SinkType里面会定义自带的Sink
HBASE("org.apache.flume.sink.hbase.HBaseSink")
HIVE("org.apache.flume.sink.hive.HiveSink")
注意在获取sink class过程当中
if (!sinkType.equals(SinkType.OTHER)) {
sinkClassName = sinkType.getSinkClassName();
}
如果没有在SinkType里面配置则需要配置全类名,比如kafka需要配置:
org.apache.flume.sink.kafka.KafkaSink
*** SourceRunner和SinkRunner ***
SourceRunner和SinkRunner是对我们具体配置source和sink的包装,并且会调用
具体的方法,比如start,process,stop.(非常重要的两个类,自己查看完整代码)
sourceRunnerMap.put(comp.getComponentName(),
SourceRunner.forSource(source));
source会被包装在SourceRunner里面
如果实现PollableSource则会初始化PollableSourceRunner,比如TaildirSource
就是实现 PollableSource
因此真正启动的时候会执行PollableSourceRunner里面的start方法,
通过 PollableSourceRunner 然后调用真正source的方法。
sinkRunnerMap.put(comp.getComponentName(),
new SinkRunner(group.getProcessor()));
类似SourceRunner,Sink的流程主要在SinkRunner里面控制
SinkRunner
runner = new PollingRunner();会执行runner线程的run方法
***handleConfigurationEvent回调函数***
这个方法会启动我们配置的组件
@Subscribe
public synchronized void handleConfigurationEvent(MaterializedConfiguration conf) {
stopAllComponents();
startAllComponents(conf);
}
startAllComponents(conf);//启动组件,按下面的顺序
Starting Channel...
Starting Sink...
Starting Source...
组件的启动都是调用了supervisor.supervise
supervisor.supervise(entry.getValue(),
new SupervisorPolicy.AlwaysRestartPolicy(), LifecycleState.START);
具体启动的地方在MonitorRunnable监控线程里面
try {
logger.info("启动组件start...... : "+lifecycleAware.getClass().getSimpleName());
lifecycleAware.start();
} catch (Throwable e) {
}
source和sink实际上是会启动SourceRunner和SinkRunner
LifecycleAware是一个顶级接口,定义了组件的开始,结束以及当前状态,flume中重要组件如source,sink,channel都实现了这个接口
***KafkaSink流程***
通过KafkaSink具体代码实现来看上面的分析
在loadSinks的时候执行configure方法会根据配置设置kafka的配置参数
在startAllComponents里面会启动SourceRunner和SinkRunner
SinkRunner会调用KafkaSink的start方法初始化kafkaProducer
然后执行KafkaSink的process开始从channel中获取数据。
未完待续
5-TAILDIR监听日志文件,源码修改,新增系统名称
6-TAILDIR监听日志文件到HDFS的案例
7-TAILDIR监听日志文件到Kafka的案例
8-TAILDIR监听日志文件到ES6.X版本的案例(包括自己实现ES高版本的Sink)
代码地址
https://github.com/lizu18xz/flume-release-1.7.0
点击查看更多内容
为 TA 点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦