Flume的程序入口是org.apache.flume.node.Application#main
进入后会先进行命令行参数的解析及核对,使用的组件是org.apache.commons.cli。还是很好用的。
会从参数中获取isZkConfigured及reload两个参数,isZkConfigured是指是否使用zookeeper来存储flume任务的配置,reload是指当flume作业的配置改变了以后是否重新启动程序来加载最新的参数。默认我们不使用zookeeper,而是采用文件来存储:
然后根据reload的不同,启动方式有所不同,为false的时候就直接启动,为true的时候就监听文件的改变,一旦有改变就重新启动加载最新的配置。这里使用了google的一个组件EventBus :
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()); }
假如是reload模式,
会构造一个configurationProvider ,注意这个configurationProvider 实现了LifecycleAware接口,那么什么是LifecycleAware?flume中把任意有生命周期(有空闲、启动、停止等状态)的组件都看作LifecycleAware,比如sink、source、channel等。
然后据此会构造一个application,并注册监听eventBus。
然后调用application.start(),
public synchronized void start() { for (LifecycleAware component : components) { supervisor.supervise(component, new SupervisorPolicy.AlwaysRestartPolicy(), LifecycleState.START); } }
这里就是把每个组件交给监护人去监护。下面分析下这个监护人LifecycleSupervisor supervisor
需要注意的是这个监护人自身也是有生命周期的,也实现了LifecycleAware。
LifecycleSupervisor 有个静态内部类Supervisoree记录监护策略、期望状态。
LifecycleSupervisor 还有个静态内部类MonitorRunnable实现Runnable接口,用于根据组件的期望状态去调用组件的相应的方法。
所以org.apache.flume.lifecycle.LifecycleSupervisor#supervise方法主要就是做一些必要的检查,然后将监护信息封装进MonitorRunnable对象,然后启动一个线程去运行它。至于具体怎么运行逻辑都封装在MonitorRunnable里面。
这里为什么使用静态内部类?如果一段逻辑必须要封装独立出去,否则违反类的单一原则,但这个类又只被当前类使用,那么可以考虑内部类,如果这个内部类不访问原类的任何成员变量,那么可以考虑使用静态内部类。
使用内部类目的主要是封装更好,更好维护。
在MonitorRunnable使用这种方法保证组件在多线程环境下状态切换的安全进行。这样我们就不用在LifecycleAware类里面使用synchronized 修饰每个方法了。
synchronized (lifecycleAware) { ..... case START:lifecycleAware.start ..... case STOP:lifecycleAware.stop ..... }
MonitorRunnable使用ScheduledThreadPoolExecutor定时调度运行,来确保每隔几秒钟就对组件来进行一次温暖的监护,确保其位于期望的状态。
到目前为止,我们的组件只有一个,就是上面的configurationProvider ,通过上面的流程我们把它启动了,那他的start方法都做了什么呢?
@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"); }
可以看到,在start时候,它起了一个周期调用线程executorService,这个周期调用线程又回每隔30s调用fileWatcherRunnable这个配置文件监控线程,在FileWatcherRunnable这里面,会去监听flume配置文件的变化,如果修改时间发生变化,eventBus会说我感兴趣的事件发生了!即eventBus.post(getConfiguration())
@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); } } }
在application中,用注解@Subscribe
标明的方法就告诉了我们,事件发生后,如何处理
@Subscribe public synchronized void handleConfigurationEvent(MaterializedConfiguration conf) { stopAllComponents(); startAllComponents(conf); }
到这里为止,讲清楚了如果启动flume时候配置了no-reload-con参数,flume就会动态加载配置文件,默认每30秒检查一次配置文件,如果有修改,会重启所有的components;如果没有配置该参数,则只会启动一次。