flume源码学习笔记-1.源码整体结构

flume源码是从github上面拉下来的,我拉的是master分支的代码,pom.xml文件里面显示是1.10版本。

一、源码主要模块说明

  • flume-ng-channels 里面包含了filechannel,jdbcchannel,kafkachannel,memorychannel通道的实现。

  • flume-ng-source 里面包含了jms, kafka, scribe, taildir, twitter 等数据源的实现。

  • flume-ng-sink 里面包含了dataset, hdfs, hive, http, irc, elasticsearch, hbase, kafka, solr等实现。

  • flume-ng-clients 实现了log4j相关的几个Appender,使得log4j的日志输出可以直接发送给flume-agent;其中有一个LoadBalancingLog4jAppender的实现,提供了多个flume-agent的load balance和ha功能,采用flume作为日志收集的可以考虑将这个appender引入内部的log4j中。

  • flume-ng-configuration 这个主要就是Flume配置信息相关的类,包括载入flume-config.properties配置文件并解析。其中包括了Source的配置,Sink的配置,Channel的配置,在阅读源码前推荐先梳理这部分关系再看其他部分的。

  • flume-ng-core flume整个核心框架,包括了各个模块的接口以及逻辑关系实现。其中instrumentation是flume内部实现的一套metric机制,metric的变化和维护,其核心也就是在MonitoredCounterGroup中通过一个Map<key, AtomicLong>来实现metric的计量。ng-core下几乎大部分代码任然几种在channel、sink、source几个子目录下,其他目录基本完成一个util和辅助的功能。

  • flume-ng-node 实现启动flume的一些基本类,包括main函数的入口(Application.java中)。在理解configuration之后,从application的main函数入手,可以较快的了解整个flume的代码。

  • flume-shared 里面包含了kafka ssl 工具类的实现。

  • flume-tools 里面包含了FlumeToolsMain

二、flume启动文件

位置:bin/flume-ng

################################
# constants
################################

FLUME_AGENT_CLASS="org.apache.flume.node.Application"
FLUME_AVRO_CLIENT_CLASS="org.apache.flume.client.avro.AvroCLIClient"
FLUME_VERSION_CLASS="org.apache.flume.tools.VersionInfo"
FLUME_TOOLS_CLASS="org.apache.flume.tools.FlumeToolsMain"

run_flume() {
  local FLUME_APPLICATION_CLASS

  if [ "$#" -gt 0 ]; then
    FLUME_APPLICATION_CLASS=$1
    shift
  else
    error "Must specify flume application class" 1
  fi

  if [ ${CLEAN_FLAG} -ne 0 ]; then
    set -x
  fi
  $EXEC $JAVA_HOME/bin/java $JAVA_OPTS $FLUME_JAVA_OPTS "${arr_java_props[@]}" -cp "$FLUME_CLASSPATH" \
      -Djava.library.path=$FLUME_JAVA_LIBRARY_PATH "$FLUME_APPLICATION_CLASS" $*
}

################################
# main
################################

# set default params
#启动flume所需的依赖,包括主目录lib下的jar包、conf目录、自定义的插件(放到plugins.d)等
FLUME_CLASSPATH=""
#启动flume的入口(一般是:org.apache.flume.node.Application)
FLUME_JAVA_LIBRARY_PATH=""
#java虚拟机参数"(-Xmx20m)
JAVA_OPTS="-Xmx20m"
LD_LIBRARY_PATH=""

opt_conf=""
opt_classpath=""
opt_plugins_dirs=""
arr_java_props=()
arr_java_props_ct=0
opt_dryrun=""

mode=$1
shift
# 根据不同的参数,确定启动模式
case "$mode" in
  help)
    display_help
    exit 0
    ;;
  agent)
    opt_agent=1
    ;;
  node)
    opt_agent=1
    warn "The \"node\" command is deprecated. Please use \"agent\" instead."
    ;;
  avro-client)
    opt_avro_client=1
    ;;
  tool)
    opt_tool=1
    ;;
  version)
   opt_version=1
   CLEAN_FLAG=0
   ;;
  *)
    error "Unknown or unspecified command '$mode'"
    echo
    display_help
    exit 1
    ;;
esac

# finally, invoke the appropriate command
# 根据不同的参数,执行不同的启动类,每个常量所对应的类路径看上面变量。
if [ -n "$opt_agent" ] ; then
  run_flume $FLUME_AGENT_CLASS $args
elif [ -n "$opt_avro_client" ] ; then
  run_flume $FLUME_AVRO_CLIENT_CLASS $args
elif [ -n "${opt_version}" ] ; then
  run_flume $FLUME_VERSION_CLASS $args
elif [ -n "${opt_tool}" ] ; then
  run_flume $FLUME_TOOLS_CLASS $args
else
  error "This message should never appear" 1
fi


从启动脚本看,有四种启动模式,分别对应的四种启动类:

FLUME_AGENT_CLASS="org.apache.flume.node.Application"
FLUME_AVRO_CLIENT_CLASS="org.apache.flume.client.avro.AvroCLIClient"
FLUME_VERSION_CLASS="org.apache.flume.tools.VersionInfo"
FLUME_TOOLS_CLASS="org.apache.flume.tools.FlumeToolsMain"

 命令: ./flume-ng version

调用的是org.apache.flume.tools.VersionInfo类

 命令: ./flume-ng tool FCINTEGRITYTOOL --dataDirs <arg>

调用的是org.apache.flume.tools.FlumeToolsMain类

从上面代码中可以看出FlumeToolType枚举类中列出了Tool命令可以调用的策略

从FlumeToolType枚举类中看到只有一种策略,调用的是org.apache.flume.tools.FileChannelIntegrityTool。

FileChannelIntegrityTool.java

FileChannelIntegrityTool内部有一个重要属性:eventValidator 事件验证器,默认不做任何验证工作,用户可以实现EventValidator接口自定义事件验证器,事件验证器主要对读取出来的事件做拦截验证工作。启动是时候 --eventValidator <arg>参数配置自定义事件验证器,-D <property=value> 参数为自定义事件验证器的参数。

  @Override
  public void run(String[] args) throws IOException, ParseException {
    ....解析命令行参数
    for (File dataDir : dataDirs) {
      // 拿到目录下的文件列表 拦截部分文件
      File[] dataFiles = dataDir.listFiles(new FilenameFilter() {
        @Override
        public boolean accept(File dir, String name) {
          // METADATA_FILENAME = ".meta";
          // METADATA_TMP_FILENAME = ".tmp";
          // OLD_METADATA_FILENAME = METADATA_FILENAME + ".old";
          // FILE_LOCK = "in_use.lock";
          if (!name.endsWith(Serialization.METADATA_FILENAME)
              && !name.endsWith(Serialization.METADATA_TMP_FILENAME)
              && !name.endsWith(Serialization.OLD_METADATA_FILENAME)
              && !name.equals(Log.FILE_LOCK)) {
            return true;
          }
          return false;
        }
      });
      if (dataFiles != null && dataFiles.length > 0) {
        for (File dataFile : dataFiles) {
          // TODO: 对每个文件进行操作
          LOG.info("Checking for corruption in " + dataFile.toString());
          // TODO: 这里主要是测试LogFileV3.SequentialReader 和 LogFile.OperationRecordUpdater
          LogFile.SequentialReader reader = new LogFileV3.SequentialReader(dataFile, null,
                  true);
          LogFile.OperationRecordUpdater updater = new LogFile.OperationRecordUpdater(dataFile);
          boolean fileDone = false;
          boolean fileBackedup = false;
          while (!fileDone) {

            long eventPosition = 0;
            try {
              // This depends on the fact that events are of the form: Type, length, data.
              // 这取决于事件的形式:类型,长度,数据。
              eventPosition = reader.getPosition();
              // Try to get the record, if the checksums don't match,
              // this will throw a CorruptEventException - so the real logic is in the catch block below.
              // 尝试获取记录,如果校验和不匹配,
              // 这将引发CorruptEventException 因此,真正的逻辑在下面的catch块中。
              LogRecord record = reader.next();
              totalChannelEvents++;
              if (record != null) {
                // 取出事务事件
                TransactionEventRecord recordEvent = record.getEvent();
                Event event = EventUtils.getEventFromTransactionEvent(recordEvent);
                if (event != null) {
                  totalPutEvents++;
                  try {
                    // 这里对每个事务事件进行特定逻辑的验证
                    if (!eventValidator.validateEvent(event)) {
                      if (!fileBackedup) {
                        Serialization.copyFile(dataFile, new File(dataFile.getParent(),
                                dataFile.getName() + ".bak"));
                        fileBackedup = true;
                      }
                      // 无效事件
                      invalidEvents++;
                      updater.markRecordAsNoop(eventPosition);
                    } else {
                      validEvents++;
                    }
                  } catch (Exception e) {
                    // OOPS, didn't expected an exception
                    // considering as failure case
                    // marking as noop
                    System.err.println("Encountered Exception while validating event, " +
                                       "marking as invalid");
                    updater.markRecordAsNoop(eventPosition);
                    eventsWithException++;
                  }
                }
              } else {
                // 没有事件了
                fileDone = true;
              }
            } catch (CorruptEventException e) {
              // 获取下一个事件发生错误
              corruptEvents++;
              totalChannelEvents++;
              LOG.warn("Corruption found in " + dataFile.toString() + " at " + eventPosition);
              if (!fileBackedup) {
                Serialization.copyFile(dataFile, new File(dataFile.getParent(),
                                                          dataFile.getName() + ".bak"));
                fileBackedup = true;
              }
              updater.markRecordAsNoop(eventPosition);
            }
          }
          updater.close();
          reader.close();
        }
      }
    }
    printSummary();
  }

FileChannelIntegrityTool的主要逻辑就是读取文件中的事件,然后每个事件通过事件验证器执行验证逻辑。最后输出统计结果。

 

实验: 准备文件

 

输出:

---------- Summary --------------------
Number of Events in the Channel = 37
Number of Put Events Processed = 25
Number of Valid Put Events = 25
Number of Invalid Put Events = 0
Number of Put Events that threw Exception during validation = 0
Number of Corrupt Events = 0
---------------------------------------

总结:这次主要是了解flume源码结构,并分析了两个flume无关痛痒的功能。这两个功能一般都是平时测试排错中用到,生产环境下主要还是用"org.apache.flume.node.Application"、"org.apache.flume.client.avro.AvroCLIClient"这两个类启动flume。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值