Apache Flume1.9.0源码走读之flume agent启动流程

 一、入口

找FLume启动入口最简单的办法,当然就是查看启动脚本。可以随便下载一个flume的tar包,在bin目录下查看flume-ng脚本。
 
这些就是flume的启动类,其中FlUME_AGNET_CLASS变量代表的就是flume-ng启动时的启动类。
入口类找到了,就先来看看当启动flume的时候都启动了哪些组件,这些组件的作用都是什么。
 

二、主方法简述

2.1 参数初始化

 
这一行截止,上面的部分是相关启动参数的初始化部分,下面是运行环境的部署
 

2.1 配置文件初始化

if (isZkConfigured) {
  //如果配置文件的存储是使用zk  
  // 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 {
  //使用本地文件来存储conf文件  
  File configurationFile = new File(commandLine.getOptionValue('f'));

  /*
   * The following is to ensure that by default the agent will fail on
   * startup if the file does not exist.
   */
  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();
 

三、配置文件加载

加载Source,Channel,Sink
新版本的flume 1.9.0可以使用zk来进行配置文件的管理,那么,当加载组件的时候就有那么几种方式
  • 当配置文件存储在zk时
    • 当配置文件需要reload时
    • 当配置文件不需要reload时
  • 当配置文件存储在本地时
    • 当配置文件需要reload时
    • 当配置文件不需要reload时
判断逻辑的主干还是在main方法中,这里不再重复引用。主要看一下当上面的情况发生时具体的加载逻辑是什么样的。
 

3.1 当配置文件存储在zk时并且配置文件需要reload

这里需要提一句的是,是否存储在zk需要看是否配置了相应的zk连接,是否需要重新加载,要看是否配置了一个参数no-reload-conf,如果配置了当前参数,则不需要重新加载。需要注意的时,配置和不配置no-reload-conf区别很大。当配置了no-reload-conf这个参数的时候,意味这flume将不会监听配置文件的动态变化,如果想变更配置,只能手动重启flume。而配置了no-reload-conf这个参数,flume会默认每隔1s去判断配置文件是否发生变更,以达到更新配置文件的目的。
 boolean isZkConfigured = false;
if (commandLine.hasOption('z') || commandLine.hasOption("zkConnString")) {
  isZkConfigured = true;
}
boolean reload = !commandLine.hasOption("no-reload-conf"); 
以下是具体的调用逻辑
//如果需要重新加载配置文件
EventBus eventBus = new EventBus(agentName + "-event-bus");
List<LifecycleAware> components = Lists.newArrayList();
//注册当前配置的Provider
PollingZooKeeperConfigurationProvider zookeeperConfigurationProvider =
    new PollingZooKeeperConfigurationProvider(
        agentName, zkConnectionStr, baseZkPath, eventBus);
//将provider加入到一个数组中,components是一个arrayList集合        
components.add(zookeeperConfigurationProvider);
//将组件重新注册
application = new Application(components);
eventBus.register(application);
 
在这里FLume引用了一个外部的组件EventBus,对于这种不知道的东西怎么办,看注释
一个轻量级的发布-订阅的库,那么flume到底是用他来干了什么呢,接着向下看,至于EventBus到底是怎样实现的,它有哪些优势呢,这个之后再说。
ps:其实在这一想,发布-订阅,难道flume的消息队列就是用这个来实现的?其实不是,flume只是用它来通知加载配置文件
 
看一下PollingZooKeeperConfigurationProvider这个对象
它实现了LifecycleAware接口,这是FLume内部控制组件生命周期的统一接口,里面就三个方法
start 开始
stop 结束
getLifecycleState 获取组件状态
 
看里面的方法实现是用来更新Flume配置文件的
@Override
public void start() {
  LOGGER.debug("Starting...");
  try {
    client.start();
    try {
      agentNodeCache = new NodeCache(client, basePath + "/" + getAgentName());
      agentNodeCache.start();
      agentNodeCache.getListenable().addListener(new NodeCacheListener() {
        @Override
        public void nodeChanged() throws Exception {
          refreshConfiguration();
        }
      });
    } catch (Exception e) {
      client.close();
      throw e;
    }
  } catch (Exception e) {
    lifecycleState = LifecycleState.ERROR;
    if (e instanceof RuntimeException) {
      throw (RuntimeException) e;
    } else {
      throw new FlumeException(e);
    }
  }
  lifecycleState = LifecycleState.START;
}

private void refreshConfiguration() throws IOException {
  LOGGER.info("Refreshing configuration from ZooKeeper");
  byte[] data = null;
  ChildData childData = agentNodeCache.getCurrentData();
  if (childData != null) {
    data = childData.getData();
  }
  //从zk读取到的内容是字节流的方式,在这将其转化为Flume内部的配置类
  flumeConfiguration = configFromBytes(data);
  eventBus.post(getConfiguration());
}

 

可以看到的是在start方法里面,他获取了相应agent在zk下的配置文件并注册了监听事件(当路径下内容发生变更时会触发调用)。作用就是当该路径下的内容发生变更时,会调用refreshConfiguration对agent节点的配置进行更新以达到热部署的目的。最终要把新的配置文件更新在eventBus中。
 

3.2 当配置文件存储在zk时并且配置文件不需要reload

StaticZooKeeperConfigurationProvider zookeeperConfigurationProvider =
    new StaticZooKeeperConfigurationProvider(
        agentName, zkConnectionStr, baseZkPath);
application = new Application();
application.handleConfigurationEvent(zookeeperConfigurationProvider.getConfiguration());
 
StaticZooKeeperConfigurationProvider就是还是读取配置文件,通过它内部的getFlumeConfiguration方法
之后就是创建一个Application,然后去处理配置文件。
 
@Subscribe
public void handleConfigurationEvent(MaterializedConfiguration conf) {
  try {
    lifecycleLock.lockInterruptibly();
    stopAllComponents();
    startAllComponents(conf);
  } catch (InterruptedException e) {
    logger.info("Interrupted while trying to handle configuration event");
    return;
  } finally {
    // If interrupted while trying to lock, we don't own the lock, so must not attempt to unlock
    if (lifecycleLock.isHeldByCurrentThread()) {
      lifecycleLock.unlock();
    }
  }
}

 

处理的过程,就是先将所有组件停掉,然后按照新的配置文件重新启动。
 

3.3 回头再看EventBus

它的作用就是用来定时的调度更新配置文件,所以,当不配置no-reload-conf参数时,就需要注册一个EventBus事件,当配置文件发生变更时,通知FLume进行重启等操作。
 

3.4 当配置文件存储在本地时并且配置文件需要reload

这部分就很好理解了,过程跟上面的一样,只不过Flume会从本地读取配置文件而不是zk
 
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); 

3.5 当配置文件存储在本地时并且配置文件不需要reload

PropertiesFileConfigurationProvider configurationProvider =
    new PropertiesFileConfigurationProvider(agentName, configurationFile);
application = new Application();
application.handleConfigurationEvent(configurationProvider.getConfiguration()); 
 

四、启动

接下来终于可以启动了,就简单的一句话
application.start();
 
具体的逻辑,下回分解。。。
posted @ 2020-09-23 17:49  窥天下而知一叶  阅读(223)  评论(0)    收藏  举报