今天我们通过阅读Flume-NG的源码来看看Flume的整个启动流程,废话不多说,翠花,上源码!!

1 主类也是启动类

在这里我贴出Application中跟启动有关的方法,其他你们可以自己看源码,毕竟源码解析解的是思路。

org.apache.flume.node.Application

  /*主函数*/
public static void main(String[] args) { try { boolean isZkConfigured = false; Options options = new Options(); Option option = new Option("n", "name", true, "the name of this agent");
option.setRequired(true);
options.addOption(option); option = new Option("f", "conf-file", true,
"specify a config file (required if -z missing)");
option.setRequired(false);
options.addOption(option); option = new Option(null, "no-reload-conf", false,
"do not reload config file if changed");
options.addOption(option); // Options for Zookeeper
option = new Option("z", "zkConnString", true,
"specify the ZooKeeper connection to use (required if -f missing)");
option.setRequired(false);
options.addOption(option); option = new Option("p", "zkBasePath", true,
"specify the base path in ZooKeeper for agent configs");
option.setRequired(false);
options.addOption(option); option = new Option("h", "help", false, "display help text");
options.addOption(option); CommandLineParser parser = new GnuParser();
CommandLine commandLine = parser.parse(options, args); if (commandLine.hasOption('h')) {
new HelpFormatter().printHelp("flume-ng agent", options, true);
return;
} String agentName = commandLine.getOptionValue('n');
boolean reload = !commandLine.hasOption("no-reload-conf"); if (commandLine.hasOption('z') || commandLine.hasOption("zkConnString")) {
isZkConfigured = true;
} Application application = null;
if (isZkConfigured) {
// get options
String zkConnectionStr = commandLine.getOptionValue('z');
String baseZkPath = commandLine.getOptionValue('p'); if (reload) {
EventBus eventBus = new EventBus(agentName + "-event-bus");
List<LifecycleAware> components = Lists.newArrayList();
PollingZooKeeperConfigurationProvider zookeeperConfigurationProvider =
new PollingZooKeeperConfigurationProvider(
agentName, zkConnectionStr, baseZkPath, eventBus);
components.add(zookeeperConfigurationProvider);
application = new Application(components);
eventBus.register(application);
} else {
StaticZooKeeperConfigurationProvider zookeeperConfigurationProvider =
new StaticZooKeeperConfigurationProvider(
agentName, zkConnectionStr, baseZkPath);
application = new Application();
application.handleConfigurationEvent(zookeeperConfigurationProvider.getConfiguration());
}
} else {
File configurationFile = new File(commandLine.getOptionValue('f'));
/*
* 确保当文件不存在时agent会启动失败
*/
if (!configurationFile.exists()) {
// If command line invocation, then need to fail fast
if (System.getProperty(Constants.SYSPROP_CALLED_FROM_SERVICE) ==
null) {
String path = configurationFile.getPath();
try {
path = configurationFile.getCanonicalPath();
} catch (IOException ex) {
logger.error("Failed to read canonical path for file: " + path,
ex);
}
throw new ParseException(
"The specified configuration file does not exist: " + path);
}
}
List<LifecycleAware> components = Lists.newArrayList(); if (reload) {
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());
}
}
application.start(); final Application appReference = application;
Runtime.getRuntime().addShutdownHook(new Thread("agent-shutdown-hook") {
@Override
public void run() {
appReference.stop();
}
}); } catch (Exception e) {
logger.error("A fatal error occurred while running. Exception follows.", e);
}
} /*启动方法*/
public synchronized void start() {
for (LifecycleAware component : components) {
supervisor.supervise(component,
new SupervisorPolicy.AlwaysRestartPolicy(), LifecycleState.START);
}
} /*响应EventBus的方法*/
@Subscribe
public synchronized void handleConfigurationEvent(MaterializedConfiguration conf) {
stopAllComponents();
startAllComponents(conf);
} /*停止方法*/
public synchronized void stop() {
supervisor.stop();
if (monitorServer != null) {
monitorServer.stop();
}
} /*停止所有组件*/
private void stopAllComponents() {
if (this.materializedConfiguration != null) {
logger.info("Shutting down configuration: {}", this.materializedConfiguration);
for (Entry<String, SourceRunner> entry :
this.materializedConfiguration.getSourceRunners().entrySet()) {
try {
logger.info("Stopping Source " + entry.getKey());
supervisor.unsupervise(entry.getValue());
} catch (Exception e) {
logger.error("Error while stopping {}", entry.getValue(), e);
}
} for (Entry<String, SinkRunner> entry :
this.materializedConfiguration.getSinkRunners().entrySet()) {
try {
logger.info("Stopping Sink " + entry.getKey());
supervisor.unsupervise(entry.getValue());
} catch (Exception e) {
logger.error("Error while stopping {}", entry.getValue(), e);
}
} for (Entry<String, Channel> entry :
this.materializedConfiguration.getChannels().entrySet()) {
try {
logger.info("Stopping Channel " + entry.getKey());
supervisor.unsupervise(entry.getValue());
} catch (Exception e) {
logger.error("Error while stopping {}", entry.getValue(), e);
}
}
}
if (monitorServer != null) {
monitorServer.stop();
}
} /*启动所有组件*/
private void startAllComponents(MaterializedConfiguration materializedConfiguration) {
logger.info("Starting new configuration:{}", materializedConfiguration); this.materializedConfiguration = materializedConfiguration;
/*启动Channel*/
for (Entry<String, Channel> entry :
materializedConfiguration.getChannels().entrySet()) {
try {
logger.info("Starting Channel " + entry.getKey());
supervisor.supervise(entry.getValue(),
new SupervisorPolicy.AlwaysRestartPolicy(), LifecycleState.START);
} catch (Exception e) {
logger.error("Error while starting {}", entry.getValue(), e);
}
} /*
* Wait for all channels to start.
*/
for (Channel ch : materializedConfiguration.getChannels().values()) {
while (ch.getLifecycleState() != LifecycleState.START
&& !supervisor.isComponentInErrorState(ch)) {
try {
logger.info("Waiting for channel: " + ch.getName() +
" to start. Sleeping for 500 ms");
Thread.sleep(500);
} catch (InterruptedException e) {
logger.error("Interrupted while waiting for channel to start.", e);
Throwables.propagate(e);
}
}
} /*启动SinkRunner*/
for (Entry<String, SinkRunner> entry : materializedConfiguration.getSinkRunners().entrySet()) {
try {
logger.info("Starting Sink " + entry.getKey());
supervisor.supervise(entry.getValue(),
new SupervisorPolicy.AlwaysRestartPolicy(), LifecycleState.START);
} catch (Exception e) {
logger.error("Error while starting {}", entry.getValue(), e);
}
}
/*启动SourceRunner*/
for (Entry<String, SourceRunner> entry :
materializedConfiguration.getSourceRunners().entrySet()) {
try {
logger.info("Starting Source " + entry.getKey());
supervisor.supervise(entry.getValue(),
new SupervisorPolicy.AlwaysRestartPolicy(), LifecycleState.START);
} catch (Exception e) {
logger.error("Error while starting {}", entry.getValue(), e);
}
} this.loadMonitoring();
}

1)40行:检查Shell命令,如果含有'h'字符则返回帮助命令

2)52行:判断命令行中是否有ZooKeeper相关信息,如果有则获取zookeeper相关信息,通过PollingZooKeeperConfigurationProvider或者StaticZooKeeperConfigurationProvider 去调用存储在zookeeper中的配置信息,如果没有的话则调用PollingPropertiesFileConfigurationProvider或者PropertiesFileConfigurationProvider去指定路径读取配置文件进行加载。

3)57行和96行:无论从zookeeper中还是file中获取配置文件,都需要判断命令行中是否有“no-reload-conf”,根据reload = !commandLine.hasOption("no-reload-conf")获得结果,如果reload为真,那么程序会每隔30秒检查一次配置文件,如果检查到配置文件发生变化则关闭原有组件,重新启动,这部分细节在XXProvider中体现。

4)如果是动态轮询的方式,那么会将application注册到EventBus中,然后调用Application类中的start()来启动components,如果是只加载一次的话,则使用handleConfigurationEvent()来启动。handleConfigurationEvent()同时也是EventBus的回调函数,当eventbus执行post时,该方法就会被调用。

5)所有Application的start()实际上调用了XXProvider中的start(),来执行真正组件的启动。

6)无论是reload还是一次加载,我们可以看到的是都是调用handleConfigurationEvent()来执行,该方法最终是调用stopAllComponents()和startAllComponent来完成的

7)stopAllComponent我们就不说了,重点来看看startAllComponents,它的启动顺序是有讲究的,先启动channels,再启动SinkRunner,最后启动SourceRunner。

这里面有两个问题:

1)启动顺序的问题:为什么要先启动channel,因为sink和source的连接纽带就是channel,而且sink和source的启动都要判断是否存在连接channel,所以channel要先启动,至于sink比source先启动,我认为应该是sink作为最后消费者,而source作为数据来源,那么防止数据堆积在channel中,所以先启动消费者,再启动生产者,需求促进供给嘛,瞎扯的。

2)SourceRunner和SinkRunner是什么玩意,source和sink哪里去了,这里就简单说下,SourceRunner和SinkRunner是用于启动source和sink的驱动类,我们在下一篇source、sink和channel的分析中再来细说

2 XXProvider

该类是启动组件时主要的类,这里就以PollingPropertiesFileConfigurationProvider举例说明,上代码

public class PollingPropertiesFileConfigurationProvider
extends PropertiesFileConfigurationProvider
implements LifecycleAware { private static final Logger LOGGER =
LoggerFactory.getLogger(PollingPropertiesFileConfigurationProvider.class); private final EventBus eventBus;
private final File file;
private final int interval;
private final CounterGroup counterGroup;
private LifecycleState lifecycleState; private ScheduledExecutorService executorService; public PollingPropertiesFileConfigurationProvider(String agentName,
File file, EventBus eventBus, int interval) {
super(agentName, file);
this.eventBus = eventBus;
this.file = file;
this.interval = interval;
counterGroup = new CounterGroup();
lifecycleState = LifecycleState.IDLE;
} @Override
public void start() {
LOGGER.info("Configuration provider starting"); Preconditions.checkState(file != null,
"The parameter file must not be null"); executorService = Executors.newSingleThreadScheduledExecutor(
new ThreadFactoryBuilder().setNameFormat("conf-file-poller-%d")
.build()); FileWatcherRunnable fileWatcherRunnable =
new FileWatcherRunnable(file, counterGroup); executorService.scheduleWithFixedDelay(fileWatcherRunnable, 0, interval,
TimeUnit.SECONDS); lifecycleState = LifecycleState.START; LOGGER.debug("Configuration provider started");
} @Override
public void stop() {
LOGGER.info("Configuration provider stopping"); executorService.shutdown();
try {
while (!executorService.awaitTermination(500, TimeUnit.MILLISECONDS)) {
LOGGER.debug("Waiting for file watcher to terminate");
}
} catch (InterruptedException e) {
LOGGER.debug("Interrupted while waiting for file watcher to terminate");
Thread.currentThread().interrupt();
}
lifecycleState = LifecycleState.STOP;
LOGGER.debug("Configuration provider stopped");
} @Override
public synchronized LifecycleState getLifecycleState() {
return lifecycleState;
} @Override
public String toString() {
return "{ file:" + file + " counterGroup:" + counterGroup + " provider:"
+ getClass().getCanonicalName() + " agentName:" + getAgentName() + " }";
} public class FileWatcherRunnable implements Runnable { private final File file;
private final CounterGroup counterGroup; private long lastChange; public FileWatcherRunnable(File file, CounterGroup counterGroup) {
super();
this.file = file;
this.counterGroup = counterGroup;
this.lastChange = 0L;
} @Override
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());
} catch (Exception e) {
LOGGER.error("Failed to load configuration data. Exception follows.",
e);
} catch (NoClassDefFoundError e) {
LOGGER.error("Failed to start agent because dependencies were not " +
"found in classpath. Error follows.", e);
} catch (Throwable t) {
// caught because the caller does not handle or log Throwables
LOGGER.error("Unhandled error", t);
}
}
}
} }

1)我们在Application类中看看它的start()

  public synchronized void start() {
for (LifecycleAware component : components) {
supervisor.supervise(component,
new SupervisorPolicy.AlwaysRestartPolicy(), LifecycleState.START);
}
}

这里使用supervisor.supervise()来启动component,那么我就不带大家去看LifecycleSupervisor这个类,在这里就告诉大家,这个方法内部还是调用了LifecycleAware的start()来进行启动。既然讲到了LifecycleAware接口,怎么说都得看看代码

@InterfaceAudience.Public
@InterfaceStability.Stable
public interface LifecycleAware { public void start();
public void stop();
public LifecycleState getLifecycleState(); }

非常简单,就是三个方法,start()、stop()和getLifecycleState,但是flume大多数涉及启动关闭的类都实现了它。

2)我们可以看到PollingPropertiesFileConfigurationProvider也是实现了LifecycleAware接口,那么appliaction的start()实际上就是调用PollingPropertiesFileConfigurationProvider的start()

3)48行:启动了一个30秒执行一次的线程,也就是30秒检查一次配置文件。这个线程是一个内部类FileWatcherRunnable(77行)

4)99行:判断文件是否有改动,如果有则调用eventBus.post(getConfiguration()).那么订阅了事件的Application类则会调用handleConfigurationEvent()执行组件的全部关闭和重启。

5)同时我们注意到该类中是没有getConfiguration()的,该方法是它的父类AbstractConfigurationProvider中定义的,用于获取配置文件信息,这里就不带大家看了,有兴趣可以看一下。

启动流程的分析就到此为止,这里显示不了行号,尴尬

Flume-ng源码解析之启动流程的更多相关文章

  1. SpringMVC源码解析-DispatcherServlet启动流程和初始化

    在使用springmvc框架,会在web.xml文件配置一个DispatcherServlet,这正是web容器开始初始化,同时会在建立自己的上下文来持有SpringMVC的bean对象. 先从Dis ...

  2. Netty源码解析—客户端启动

    Netty源码解析-客户端启动 Bootstrap示例 public final class EchoClient { static final boolean SSL = System.getPro ...

  3. Sentinel源码解析一(流程总览)

    引言 Sentinel作为ali开源的一款轻量级流控框架,主要以流量为切入点,从流量控制.熔断降级.系统负载保护等多个维度来帮助用户保护服务的稳定性.相比于Hystrix,Sentinel的设计更加简 ...

  4. Thinkphp6源码分析之解析,Thinkphp6路由,Thinkphp6路由源码解析,Thinkphp6请求流程解析,Thinkphp6源码

    Thinkphp6源码解析之分析 路由篇-请求流程 0x00 前言: 第一次写这么长的博客,所以可能排版啊,分析啊,什么的可能会比较乱.但是我大致的流程已经觉得是说的够清楚了.几乎是每行源码上都有注释 ...

  5. 【原创】angularjs1.3.0源码解析之执行流程

    Angular执行流程 前言 发现最近angularjs在我厂的应用变得很广泛,下周刚好也有个angular项目要着手开始做,所以先做了下功课,从源代码开始入手会更深刻点,可能讲的没那么细,侧重点在于 ...

  6. Ceph源码解析:读写流程

    转载注明出处,整理也是需要功夫的,http://www.cnblogs.com/chenxianpao/p/5572859.html 一.OSD模块简介 1.1 消息封装:在OSD上发送和接收信息. ...

  7. Python Web Flask源码解读(一)——启动流程

    关于我 一个有思想的程序猿,终身学习实践者,目前在一个创业团队任team lead,技术栈涉及Android.Python.Java和Go,这个也是我们团队的主要技术栈. Github:https:/ ...

  8. Spring5源码解析1-从启动容器开始

    从启动容器开始 最简单的启动spring的代码如下: @Configuration @ComponentScan public class AppConfig { } public class Mai ...

  9. Vue.js源码解析-Vue初始化流程

    目录 前言 1. 初始化流程概述图.代码流程图 1.1 初始化流程概述 1.2 初始化代码执行流程图 2. 初始化相关代码分析 2.1 initGlobalAPI(Vue) 初始化Vue的全局静态AP ...

随机推荐

  1. 【angularjs】【学习心得】路由实战篇

    今天还是来说一下angular中的路由模块.我们实际项目中,各个页面的切换是经常会与Auth相关的.比如我网站的后台,是需要登录过的用户才能进去,那么我们用angularJS做前端路由的时候应该怎么完 ...

  2. Ubuntu 14.04下搭建Node.js的开发环境

    最近想找一个轻量级且支持快速开发的服务开发平台,选来选去选择了Node.js,当时有几种选择: Python + Django(用过Django,虽然开发快速,但是感觉性能并不太好). Ruby + ...

  3. backbone 1.1.2 api

    Backbone.js为复杂WEB应用程序提供模型(models).集合(collections).视图(views)的结构.其中模型用于绑定键值数据和自定义事件:集合附有可枚举函数的丰富API: 视 ...

  4. 这个发现是否会是RSA算法的BUG、或者可能存在的破解方式?

    笔者从事各种数据加解密算法相关的工作若干年,今天要说的是基于大数分解难题的RSA算法,可能有些啰嗦. 事情的起因是这样的,我最近针对一款芯片进行RSA CRT解密的性能优化.因为期望值是1024bit ...

  5. CodeForces 460B

    Little Dima and Equation Time Limit:1000MS     Memory Limit:262144KB     64bit IO Format:%I64d & ...

  6. Unix/Linux 网络 IO 模型简介

    概述 Linux内核将所有外部设备都看做一个文件来操作.对该文件的读写操作会调用内核提供的系统命令, 返回一个fd(file descriptor)文件描述符.而对一个socket的读写也有相应的描述 ...

  7. JavaScript 事件总结

    本文总结自<JavaScript高级程序设计>以及自己平时的经验,针对较新浏览器以及 DOM3 级事件标准(2016年8月),对少部分内容作了更正,增加了各种例子及解析. 如无特殊说明,本 ...

  8. xml类型转换列表显示 SQL查询

    数据库中存在字段类型为xml 的数据, 现举例 xml 字段存储的数据为: <MortgageInfoShipList> <ITEMS> <ITEM> <Sh ...

  9. (一) 从Angular1到Angular2的杂谈

    使用了angular1一年下来,完成了若干项目,承蒙此框架的强大带来了不算差的项目编写体验,但1.*版本的angular,确实是有厉害的地方也有其尴尬的地方,包括较多数据的渲染的性能问题,还有就是可能 ...

  10. Omi教程-生命周期和事件处理

    生命周期 名称 含义 时机 constructor 构造函数 new的时候 install 初始化安装,这可以拿到用户传进的data进行处理 实例化 installed 安装完成,HTML已经插入页面 ...