Flume 實現自己的實時日誌(2)

karspb發表於2021-09-09

目錄解讀

最近接觸到Flume,這裡透過一些小案例做一些學習的分享。主要包括以下內容:
1-概念、2-原始碼編譯、3-快速入門:
4-原始碼解讀
5-TAILDIR監聽日誌檔案,原始碼修改,新增系統名稱
6-TAILDIR監聽日誌檔案到HDFS的案例
7-TAILDIR監聽日誌檔案到Kafka的案例
8-TAILDIR監聽日誌檔案到ES6.X版本的案例(包括自己實現ES高版本的Sink)
注:本系列所有文章基於Flume 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)

程式碼地址


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2001/viewspace-2821204/,如需轉載,請註明出處,否則將追究法律責任。

相關文章