Flume架构与源码分析-核心组件分析-2

开发 开发工具
从以上部分我们可以看出,不管是Source还是Sink都依赖Channel,那么启动时应该先启动Channel然后再启动Source或Sink即可。

[[177366]]

4、整体流程

从以上部分我们可以看出,不管是Source还是Sink都依赖Channel,那么启动时应该先启动Channel然后再启动Source或Sink即可。

Flume有两种启动方式:使用EmbeddedAgent内嵌在Java应用中或使用Application单独启动一个进程,此处我们已Application分析为主。

首先进入org.apache.flume.node.Application的main方法启动:

Java代码

  1. //1、设置默认值启动参数、参数是否必须的    
  2. Options options = new Options();    
  3. Option option = new Option("n""name"true"the name of this agent");    
  4. option.setRequired(true);    
  5. options.addOption(option);    
  6.     
  7. option = new Option("f""conf-file"true,    
  8. "specify a config file (required if -z missing)");    
  9. option.setRequired(false);    
  10. options.addOption(option);    
  11.     
  12. //2、接着解析命令行参数    
  13. CommandLineParser parser = new GnuParser();    
  14. CommandLine commandLine = parser.parse(options, args);    
  15.     
  16. String agentName = commandLine.getOptionValue('n');    
  17. boolean reload = !commandLine.hasOption("no-reload-conf");    
  18.     
  19. if (commandLine.hasOption('z') || commandLine.hasOption("zkConnString")) {    
  20.   isZkConfigured = true;    
  21. }    
  22.     
  23. if (isZkConfigured) {    
  24.     //3、如果是通过ZooKeeper配置,则使用ZooKeeper参数启动,此处忽略,我们以配置文件讲解    
  25. else {    
  26.   //4、打开配置文件,如果不存在则快速失败    
  27.   File configurationFile = new File(commandLine.getOptionValue('f'));    
  28.   if (!configurationFile.exists()) {    
  29.          throw new ParseException(    
  30.         "The specified configuration file does not exist: " + path);    
  31.   }    
  32.   List<LifecycleAware> components = Lists.newArrayList();    
  33.     
  34.   if (reload) { //5、如果需要定期reload配置文件,则走如下方式    
  35.     //5.1、此处使用Guava提供的事件总线    
  36.     EventBus eventBus = new EventBus(agentName + "-event-bus");    
  37.     //5.2、读取配置文件,使用定期轮训拉起策略,默认30s拉取一次    
  38.     PollingPropertiesFileConfigurationProvider configurationProvider =    
  39.         new PollingPropertiesFileConfigurationProvider(    
  40.           agentName, configurationFile, eventBus, 30);    
  41.     components.add(configurationProvider);    
  42.     application = new Application(components); //5.3、向Application注册组件    
  43.     //5.4、向事件总线注册本应用,EventBus会自动注册Application中使用@Subscribe声明的方法    
  44.     eventBus.register(application);    
  45.     
  46.   } else { //5、配置文件不支持定期reload    
  47.     PropertiesFileConfigurationProvider configurationProvider =    
  48.         new PropertiesFileConfigurationProvider(    
  49.           agentName, configurationFile);    
  50.     application = new Application();    
  51.     //6.2、直接使用配置文件初始化Flume组件    
  52.     application.handleConfigurationEvent(configurationProvider    
  53.       .getConfiguration());    
  54.   }    
  55. }    
  56. //7、启动Flume应用    
  57. application.start();    
  58.     
  59. //8、注册虚拟机关闭钩子,当虚拟机关闭时调用Application的stop方法进行终止    
  60. final Application appReference = application;    
  61. Runtime.getRuntime().addShutdownHook(new Thread("agent-shutdown-hook") {    
  62.   @Override    
  63.   public void run() {    
  64.     appReference.stop();    
  65.   }    
  66. });    

以上流程只提取了核心代码中的一部分,比如ZK的实现直接忽略了,而Flume启动大体流程如下:

1、读取命令行参数;

2、读取配置文件;

3、根据是否需要reload使用不同的策略初始化Flume;如果需要reload,则使用Guava的事件总线实现,Application的handleConfigurationEvent是事件订阅者,PollingPropertiesFileConfigurationProvider是事件发布者,其会定期轮训检查文件是否变更,如果变更则重新读取配置文件,发布配置文件事件变更,而handleConfigurationEvent会收到该配置变更重新进行初始化;

4、启动Application,并注册虚拟机关闭钩子。

handleConfigurationEvent方法比较简单,首先调用了stopAllComponents停止所有组件,接着调用startAllComponents使用配置文件初始化所有组件:

Java代码

  1. @Subscribe    
  2. public synchronized void handleConfigurationEvent(MaterializedConfiguration conf) {    
  3.   stopAllComponents();    
  4.   startAllComponents(conf);    
  5. }     

MaterializedConfiguration存储Flume运行时需要的组件:Source、Channel、Sink、SourceRunner、SinkRunner等,其是通过ConfigurationProvider进行初始化获取,比如PollingPropertiesFileConfigurationProvider会读取配置文件然后进行组件的初始化。

对于startAllComponents实现大体如下:

Java代码

  1. //1、首先启动Channel    
  2. supervisor.supervise(Channels,    
  3.       new SupervisorPolicy.AlwaysRestartPolicy(), LifecycleState.START);    
  4. //2、确保所有Channel是否都已启动    
  5. for(Channel ch: materializedConfiguration.getChannels().values()){    
  6.   while(ch.getLifecycleState() != LifecycleState.START    
  7.       && !supervisor.isComponentInErrorState(ch)){    
  8.     try {    
  9.       Thread.sleep(500);    
  10.     } catch (InterruptedException e) {    
  11.         Throwables.propagate(e);    
  12.     }    
  13.   }    
  14. }    
  15. //3、启动SinkRunner    
  16. supervisor.supervise(SinkRunners,      
  17. new SupervisorPolicy.AlwaysRestartPolicy(), LifecycleState.START);    
  18. //4、启动SourceRunner    
  19. supervisor.supervise(SourceRunner,    
  20. new SupervisorPolicy.AlwaysRestartPolicy(), LifecycleState.START);    
  21. //5、初始化监控服务    
  22. this.loadMonitoring();    

从如下代码中可以看到,首先要准备好Channel,因为Source和Sink会操作它,对于Channel如果初始化失败则整个流程是失败的;然后启动SinkRunner,先准备好消费者;接着启动SourceRunner开始进行采集日志。此处我们发现有两个单独的组件LifecycleSupervisor和MonitorService,一个是组件守护哨兵,一个是监控服务。守护哨兵对这些组件进行守护,假设出问题了默认策略是自动重启这些组件。

对于stopAllComponents实现大体如下:

Java代码

  1. //1、首先停止SourceRunner    
  2. supervisor.unsupervise(SourceRunners);    
  3. //2、接着停止SinkRunner    
  4. supervisor.unsupervise(SinkRunners);    
  5. //3、然后停止Channel    
  6. supervisor.unsupervise(Channels);    
  7. //4、***停止MonitorService    
  8. monitorServer.stop();     

此处可以看出,停止的顺序是Source、Sink、Channel,即先停止生产,再停止消费,***停止管道。

Application中的start方法代码实现如下:

Java代码

  1. public synchronized void start() {    
  2.   for(LifecycleAware component : components) {    
  3.     supervisor.supervise(component,    
  4.         new SupervisorPolicy.AlwaysRestartPolicy(), LifecycleState.START);    
  5.   }    
  6. }     

其循环Application注册的组件,然后守护哨兵对它进行守护,默认策略是出现问题会自动重启组件,假设我们支持reload配置文件,则之前启动Application时注册过PollingPropertiesFileConfigurationProvider组件,即该组件会被守护哨兵守护着,出现问题默认策略自动重启。

而Application关闭执行了如下动作:

Java代码

  1. public synchronized void stop() {    
  2.   supervisor.stop();    
  3.   if(monitorServer != null) {    
  4.     monitorServer.stop();    
  5.   }    
  6. }     
  7.   

即关闭守护哨兵和监控服务。

到此基本的Application分析结束了,我们还有很多疑问,守护哨兵怎么实现的。

整体流程可以总结为:

1、首先初始化命令行配置;

2、接着读取配置文件;

3、根据是否需要reload初始化配置文件中的组件;如果需要reload会使用Guava事件总线进行发布订阅变化;

4、接着创建Application,创建守护哨兵,并先停止所有组件,接着启动所有组件;启动顺序:Channel、SinkRunner、SourceRunner,并把这些组件注册给守护哨兵、初始化监控服务;停止顺序:SourceRunner、SinkRunner、Channel;

5、如果配置文件需要定期reload,则需要注册Polling***ConfigurationProvider到守护哨兵;

6、***注册虚拟机关闭钩子,停止守护哨兵和监控服务。

轮训实现的SourceRunner 和SinkRunner会创建一个线程进行工作,之前已经介绍了其工作方式。接下来我们看下守护哨兵的实现。

首先创建LifecycleSupervisor:

Java代码

  1. //1、用于存放被守护的组件    
  2. supervisedProcesses = new HashMap<LifecycleAware, Supervisoree>();    
  3. //2、用于存放正在被监控的组件    
  4. monitorFutures = new HashMap<LifecycleAware, ScheduledFuture<?>>();    
  5. //3、创建监控服务线程池    
  6. monitorService = new ScheduledThreadPoolExecutor(10,    
  7.     new ThreadFactoryBuilder().setNameFormat(    
  8.         "lifecycleSupervisor-" + Thread.currentThread().getId() + "-%d")    
  9.         .build());    
  10. monitorService.setMaximumPoolSize(20);    
  11. monitorService.setKeepAliveTime(30, TimeUnit.SECONDS);    
  12. //4、定期清理被取消的组件    
  13. purger = new Purger();    
  14. //4.1、默认不进行清理    
  15. needToPurge = false;     

LifecycleSupervisor启动时会进行如下操作:

Java代码

  1. public synchronized void start() {    
  2.   monitorService.scheduleWithFixedDelay(purger, 2, 2, TimeUnit.HOURS);    
  3.   lifecycleState = LifecycleState.START;    
  4. }     

首先每隔两个小时执行清理组件,然后改变状态为启动。而LifecycleSupervisor停止时直接停止了监控服务,然后更新守护组件状态为STOP:

Java代码

  1. //1、首先停止守护监控服务    
  2. if (monitorService != null) {    
  3.   monitorService.shutdown();    
  4.   try {    
  5.     monitorService.awaitTermination(10, TimeUnit.SECONDS);    
  6.   } catch (InterruptedException e) {    
  7.     logger.error("Interrupted while waiting for monitor service to stop");    
  8.   }    
  9. }    
  10. //2、更新所有守护组件状态为STOP,并调用组件的stop方法进行停止    
  11. for (final Entry<LifecycleAware, Supervisoree> entry : supervisedProcesses.entrySet()) {    
  12.   if (entry.getKey().getLifecycleState().equals(LifecycleState.START)) {    
  13.     entry.getValue().status.desiredState = LifecycleState.STOP;    
  14.     entry.getKey().stop();    
  15.   }    
  16. }    
  17.  
  18. //3、更新本组件状态    
  19. if (lifecycleState.equals(LifecycleState.START)) {    
  20.   lifecycleState = LifecycleState.STOP;    
  21. }     
  22. //4、***的清理    
  23.  
  24. supervisedProcesses.clear();     
  25. monitorFutures.clear();     

接下来就是调用supervise进行组件守护了:

Java代码

  1.  if(this.monitorService.isShutdown() || this.monitorService.isTerminated()    
  2.   || this.monitorService.isTerminating()){    
  3.     //1、如果哨兵已停止则抛出异常,不再接收任何组件进行守护    
  4.   }    
  5.   //2、初始化守护组件    
  6.   Supervisoree process = new Supervisoree();    
  7.   process.status = new Status();    
  8.   //2.1、默认策略是失败重启    
  9.   process.policy = policy;    
  10.   //2.2、初始化组件默认状态,大多数组件默认为START    
  11.   process.status.desiredState = desiredState;    
  12.   process.status.error = false;    
  13.   //3、组件监控器,用于定时获取组件的***状态,或者重新启动组件    
  14.   MonitorRunnable monitorRunnable = new MonitorRunnable();    
  15.   monitorRunnable.lifecycleAware = lifecycleAware;    
  16.   monitorRunnable.supervisoree = process;    
  17.   monitorRunnable.monitorService = monitorService;    
  18.     
  19.   supervisedProcesses.put(lifecycleAware, process);    
  20.   //4、定期的去执行组件监控器,获取组件***状态,或者重新启动组件    
  21.   ScheduledFuture<?> future = monitorService.scheduleWithFixedDelay(    
  22.       monitorRunnable, 0, 3, TimeUnit.SECONDS);    
  23.   monitorFutures.put(lifecycleAware, future);    
  24. }    

如果不需要守护了,则需要调用unsupervise:

Java代码

  1. public synchronized void unsupervise(LifecycleAware lifecycleAware) {    
  2.   synchronized (lifecycleAware) {    
  3.     Supervisoree supervisoree = supervisedProcesses.get(lifecycleAware);    
  4.     //1.1、设置守护组件的状态为被丢弃    
  5.     supervisoree.status.discard = true;    
  6.     //1.2、设置组件盼望的***生命周期状态为STOP    
  7.     this.setDesiredState(lifecycleAware, LifecycleState.STOP);    
  8.     //1.3、停止组件    
  9.     lifecycleAware.stop();    
  10.   }    
  11.   //2、从守护组件中移除    
  12.   supervisedProcesses.remove(lifecycleAware);    
  13.   //3、取消定时监控组件服务    
  14.   monitorFutures.get(lifecycleAware).cancel(false);    
  15.   //3.1、通知Purger需要进行清理,Purger会定期的移除cancel的组件    
  16.   needToPurge = true;    
  17.   monitorFutures.remove(lifecycleAware);    
  18. }    

接下来我们再看下MonitorRunnable的实现,其负责进行组件状态迁移或组件故障恢复:

Java代码

  1. public synchronized void unsupervise(LifecycleAware lifecycleAware) {    
  2.   synchronized (lifecycleAware) {    
  3.     Supervisoree supervisoree = supervisedProcesses.get(lifecycleAware);    
  4.     //1.1、设置守护组件的状态为被丢弃    
  5.     supervisoree.status.discard = true;    
  6.     //1.2、设置组件盼望的***生命周期状态为STOP    
  7.     this.setDesiredState(lifecycleAware, LifecycleState.STOP);    
  8.     //1.3、停止组件    
  9.     lifecycleAware.stop();    
  10.   }    
  11.   //2、从守护组件中移除    
  12.   supervisedProcesses.remove(lifecycleAware);    
  13.   //3、取消定时监控组件服务    
  14.   monitorFutures.get(lifecycleAware).cancel(false);    
  15.   //3.1、通知Purger需要进行清理,Purger会定期的移除cancel的组件    
  16.   needToPurge = true;    
  17.   monitorFutures.remove(lifecycleAware);    
  18. }    
  19. 接下来我们再看下MonitorRunnable的实现,其负责进行组件状态迁移或组件故障恢复:  
  20. Java代码    
  21. public void run() {    
  22.   long now = System.currentTimeMillis();    
  23.   try {    
  24.     if (supervisoree.status.firstSeen == null) {    
  25.         supervisoree.status.firstSeen = now; //1、记录***次状态查看时间    
  26.     }    
  27.     supervisoree.status.lastSeen = now; //2、记录***一次状态查看时间    
  28.     synchronized (lifecycleAware) {    
  29.         //3、如果守护组件被丢弃或出错了,则直接返回    
  30.         if (supervisoree.status.discard || supervisoree.status.error) {    
  31.           return;    
  32.         }    
  33.         //4、更新***一次查看到的状态    
  34.         supervisoree.status.lastSeenState = lifecycleAware.getLifecycleState();    
  35.         //5、如果组件的状态和守护组件看到的状态不一致,则以守护组件的状态为准,然后进行初始化    
  36.         if (!lifecycleAware.getLifecycleState().equals(    
  37.             supervisoree.status.desiredState)) {    
  38.           switch (supervisoree.status.desiredState) {     
  39.             case START: //6、如果是启动状态,则启动组件    
  40.              try {    
  41.                 lifecycleAware.start();    
  42.               } catch (Throwable e) {    
  43.                 if (e instanceof Error) {    
  44.                   supervisoree.status.desiredState = LifecycleState.STOP;    
  45.                   try {    
  46.                     lifecycleAware.stop();    
  47.                   } catch (Throwable e1) {    
  48.                     supervisoree.status.error = true;    
  49.                     if (e1 instanceof Error) {    
  50.                       throw (Error) e1;    
  51.                     }    
  52.                   }    
  53.                 }    
  54.                 supervisoree.status.failures++;    
  55.               }    
  56.               break;    
  57.             case STOP: //7、如果是停止状态,则停止组件    
  58.               try {    
  59.                 lifecycleAware.stop();    
  60.               } catch (Throwable e) {    
  61.                 if (e instanceof Error) {    
  62.                   throw (Error) e;    
  63.                 }    
  64.                 supervisoree.status.failures++;    
  65.               }    
  66.               break;    
  67.             default:    
  68.           }    
  69.     } catch(Throwable t) {    
  70.     }    
  71.   }    
  72. }    

如上代码进行了一些简化,整体逻辑即定时去采集组件的状态,如果发现守护组件和组件的状态不一致,则可能需要进行启动或停止。即守护监视器可以用来保证组件如能失败后自动启动。默认策略是总是失败后重启,还有一种策略是只启动一次。

【本文是51CTO专栏作者张开涛的原创文章,作者微信公众号:开涛的博客,id:kaitao-1234567】

责任编辑:武晓燕 来源: 开涛的博客
相关推荐

2016-11-25 13:26:50

Flume架构源码

2016-11-25 13:14:50

Flume架构源码

2016-11-29 16:59:46

Flume架构源码

2022-06-07 10:33:29

Camera组件鸿蒙

2015-04-24 09:33:11

Cloud Found组件分析PaaS

2021-09-05 07:35:58

lifecycleAndroid组件原理

2011-04-29 13:40:37

MongoDBCommand

2009-12-31 15:55:06

ADO.NET结构

2022-01-05 08:53:13

Spring原理分析MVC

2016-10-21 13:03:18

androidhandlerlooper

2021-09-08 10:47:33

Flink执行流程

2009-06-10 13:19:21

J2EE核心APIJ2EE核心组件

2019-10-08 10:01:22

Kafka应用场景架构

2017-05-04 22:30:17

Zuul过滤器微服务

2015-08-11 15:52:52

大数据数据分析

2022-07-17 06:51:22

Vite 3.0前端

2020-08-06 08:16:26

Kubernetes架构开源

2022-03-18 15:55:15

鸿蒙操作系统架构

2019-10-16 16:33:41

Docker架构语言

2020-12-25 10:09:29

代码开发平台
点赞
收藏

51CTO技术栈公众号