最近项目在用到flume,因此翻了下flume的代码,

启动脚本:

  nohup bin/flume-ng agent -n tsdbflume -c conf -f conf/配置文件.conf -Dflume.root.logger=DEBUG,console &

翻下flume-ng的脚本,  FLUME_AGENT_CLASS="org.apache.flume.node.Application"

从Application进入;

写了下flume-agent启动的时序图:

这是简单的启动时序图,时序图里画的都在flume-node这个项目里;

里边其实最核心的是创建配置文件里的对象,这个工作是在AbstractConfigurationProvider.getConfiguration()这个方法里做的。先上代码

  public MaterializedConfiguration getConfiguration() {
MaterializedConfiguration conf = new SimpleMaterializedConfiguration();
   // getFlumeConfiguration()方法,是关键核心,负责整个配置加载,下边代码说明
FlumeConfiguration fconfig = getFlumeConfiguration();
AgentConfiguration agentConf = fconfig.getConfigurationFor(getAgentName());
if (agentConf != null) {
Map<String, ChannelComponent> channelComponentMap = Maps.newHashMap();
Map<String, SourceRunner> sourceRunnerMap = Maps.newHashMap();
Map<String, SinkRunner> sinkRunnerMap = Maps.newHashMap();
try {
loadChannels(agentConf, channelComponentMap);
loadSources(agentConf, channelComponentMap, sourceRunnerMap);
loadSinks(agentConf, channelComponentMap, sinkRunnerMap);
Set<String> channelNames = new HashSet<String>(channelComponentMap.keySet());
for (String channelName : channelNames) {
ChannelComponent channelComponent = channelComponentMap.get(channelName);
if (channelComponent.components.isEmpty()) {
LOGGER.warn(String.format("Channel %s has no components connected" +
" and has been removed.", channelName));
channelComponentMap.remove(channelName);
Map<String, Channel> nameChannelMap =
channelCache.get(channelComponent.channel.getClass());
if (nameChannelMap != null) {
nameChannelMap.remove(channelName);
}
} else {
LOGGER.info(String.format("Channel %s connected to %s",
channelName, channelComponent.components.toString()));
conf.addChannel(channelName, channelComponent.channel);
}
}
for (Map.Entry<String, SourceRunner> entry : sourceRunnerMap.entrySet()) {
conf.addSourceRunner(entry.getKey(), entry.getValue());
}
for (Map.Entry<String, SinkRunner> entry : sinkRunnerMap.entrySet()) {
conf.addSinkRunner(entry.getKey(), entry.getValue());
}
} catch (InstantiationException ex) {
LOGGER.error("Failed to instantiate component", ex);
} finally {
channelComponentMap.clear();
sourceRunnerMap.clear();
sinkRunnerMap.clear();
}
} else {
LOGGER.warn("No configuration found for this host:{}", getAgentName());
}
return conf;
}

下面说下这个类的具体加载过程。

PropertiesFileConfigurationProvider.getFlumeConfiguration()

  @Override
public FlumeConfiguration getFlumeConfiguration() {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(file));
Properties properties = new Properties();
properties.load(reader);
return new FlumeConfiguration(toMap(properties));
} catch (IOException ex) {
LOGGER.error("Unable to load file:" + file
+ " (I/O failure) - Exception follows.", ex);
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException ex) {
LOGGER.warn(
"Unable to close file reader for file: " + file, ex);
}
}
}
return new FlumeConfiguration(new HashMap<String, String>());
}

new FlumeConfiguration(toMap(properties)),代码在下边:

下边从代码入手写下怎么加载内容:

  /**
* 创建一个FlumeConfiguration对象,参数为:配置文件的key-value对
* Creates a populated Flume Configuration object.
*/
public FlumeConfiguration(Map<String, String> properties) {
agentConfigMap = new HashMap<String, AgentConfiguration>();
errors = new LinkedList<FlumeConfigurationError>();
// Construct the in-memory component hierarchy
for (String name : properties.keySet()) {
String value = properties.get(name); // addRawProperty里对agentConfigMap初始化,
// 1:这里插入的是agentConfiguration对象里的contextMap
if (!addRawProperty(name, value)) {
logger.warn("Configuration property ignored: " + name + " = " + value);
}
}
// Now iterate thru the agentContext and create agent configs and add them
// to agentConfigMap // validate and remove improperly configured components
// 2:这里插入的是agentConfiguration对象里的configMap
validateConfiguration();
}

然后,进入FlumeConfiguration.addRawProperty(name,value):

  private boolean addRawProperty(String name, String value) {
// Null names and values not supported
if (name == null || value == null) {
errors
.add(new FlumeConfigurationError("", "",
FlumeConfigurationErrorType.AGENT_NAME_MISSING,
ErrorOrWarning.ERROR));
return false;
} // Empty values are not supported
if (value.trim().length() == 0) {
errors
.add(new FlumeConfigurationError(name, "",
FlumeConfigurationErrorType.PROPERTY_VALUE_NULL,
ErrorOrWarning.ERROR));
return false;
} // Remove leading and trailing spaces
name = name.trim();
value = value.trim(); int index = name.indexOf('.'); // All configuration keys must have a prefix defined as agent name
if (index == -1) {
errors
.add(new FlumeConfigurationError(name, "",
FlumeConfigurationErrorType.AGENT_NAME_MISSING,
ErrorOrWarning.ERROR));
return false;
} String agentName = name.substring(0, index); // Agent name must be specified for all properties
if (agentName.length() == 0) {
errors
.add(new FlumeConfigurationError(name, "",
FlumeConfigurationErrorType.AGENT_NAME_MISSING,
ErrorOrWarning.ERROR));
return false;
} String configKey = name.substring(index + 1); // Configuration key must be specified for every property
if (configKey.length() == 0) {
errors
.add(new FlumeConfigurationError(name, "",
FlumeConfigurationErrorType.PROPERTY_NAME_NULL,
ErrorOrWarning.ERROR));
return false;
} AgentConfiguration aconf = agentConfigMap.get(agentName); // 这里创建AgentConfiguration,并插入到FlumeConfiguration的Map<String, AgentConfiguration> agentConfigMap中
if (aconf == null) {
aconf = new AgentConfiguration(agentName, errors);
agentConfigMap.put(agentName, aconf);
} // Each configuration key must begin with one of the three prefixes:
// sources, sinks, or channels.
// 最终,键值对被加载到agentConfiguration中
return aconf.addProperty(configKey, value);
}

最终被加载到:agentConfiguration.addProperty(String key, String value)中

      ComponentNameAndConfigKey cnck = parseConfigKey(key,
BasicConfigurationConstants.CONFIG_SOURCES_PREFIX); if (cnck != null) {
// it is a source
String name = cnck.getComponentName();
Context srcConf = sourceContextMap.get(name); if (srcConf == null) {
srcConf = new Context();
sourceContextMap.put(name, srcConf);
}
// sourceContextMap中存放new Context().put(cnck.getConifyKey(),value)
srcConf.put(cnck.getConfigKey(), value);
return true;
}

所以最后是加载到AgentConfiguration中对应的*ContextMap中了;

  public static class AgentConfiguration {

    private final String agentName;
private String sources;
private String sinks;
private String channels;
private String sinkgroups; private final Map<String, ComponentConfiguration> sourceConfigMap;
private final Map<String, ComponentConfiguration> sinkConfigMap;
private final Map<String, ComponentConfiguration> channelConfigMap;
private final Map<String, ComponentConfiguration> sinkgroupConfigMap; private Map<String, Context> sourceContextMap;
private Map<String, Context> sinkContextMap;
private Map<String, Context> channelContextMap;
private Map<String, Context> sinkGroupContextMap; private Set<String> sinkSet;
private Set<String> sourceSet;
private Set<String> channelSet;
private Set<String> sinkgroupSet; private final List<FlumeConfigurationError> errorList; // ** 省略其他代码 }

这里比较崩溃,跑来跑去的我们来梳理下:

先是FlumeConfiguration的addRawProperty方法执行put动作:

  FlumeConfiguration.agentConfigMap.put(agentName, AgentConfiguration conf);

在这里创建AgentCponfiguration对象

  aconf = new AgentCponfiguration(agentName, errors);

然后执行

  aconf.addProperty(configKey, value), 将key,value插入到AgentConfiguration对象的*ContextMap中,至此完成键值对的保存工作;

最终所有的键值对都被保存在AgentConfiguration对象中;

对于AgentConfiguration的插入操作,也分为两个部分,在FlumeConifiguration构造方法中,

1:addRawProperty(name,value);将kv键值对,插入到AgentConfiguration对象的contextMap中;

2:validateConfiguration;下边跟进下validateConfiguration代码;

validateConfiguration -> aconf.isValid() -> validateChannels(channelSet)

代码注释中,其实已经写得比较详细了

    /**
* If it is a known component it will do the full validation required for
* that component, else it will do the validation required for that class.
*/
private Set<String> validateChannels(Set<String> channelSet) {
Iterator<String> iter = channelSet.iterator();
Map<String, Context> newContextMap = new HashMap<String, Context>();
ChannelConfiguration conf = null;
/*
* The logic for the following code:
*
* Is it a known component?
* -Yes: Get the ChannelType and set the string name of that to
* config and set configSpecified to true.
* -No.Look for config type for the given component:
* -Config Found:
* Set config to the type mentioned, set configSpecified to true
* -No Config found:
* Set config to OTHER, configSpecified to false,
* do basic validation. Leave the context in the
* contextMap to process later. Setting it to other returns
* a vanilla configuration(Source/Sink/Channel Configuration),
* which does basic syntactic validation. This object is not
* put into the map, so the context is retained which can be
* picked up - this is meant for older classes which don't
* implement ConfigurableComponent.
*/
while (iter.hasNext()) {
String channelName = iter.next();
Context channelContext = channelContextMap.get(channelName);
// Context exists in map.
if (channelContext != null) {
// Get the configuration object for the channel:
ChannelType chType = getKnownChannel(channelContext.getString(
BasicConfigurationConstants.CONFIG_TYPE));
boolean configSpecified = false;
String config = null;
// Not a known channel - cannot do specific validation to this channel
if (chType == null) {
config = channelContext.getString(BasicConfigurationConstants.CONFIG_CONFIG);
if (config == null || config.isEmpty()) {
config = "OTHER";
} else {
configSpecified = true;
}
} else {
config = chType.toString().toUpperCase(Locale.ENGLISH);
configSpecified = true;
} try {
conf =
(ChannelConfiguration) ComponentConfigurationFactory.create(
channelName, config, ComponentType.CHANNEL);
logger.debug("Created channel " + channelName);
if (conf != null) {
conf.configure(channelContext);
}
if ((configSpecified && conf.isNotFoundConfigClass()) ||
!configSpecified) {
newContextMap.put(channelName, channelContext);
} else if (configSpecified) {
channelConfigMap.put(channelName, conf);
}
if (conf != null) {
errorList.addAll(conf.getErrors());
}
} catch (ConfigurationException e) {
// Could not configure channel - skip it.
// No need to add to error list - already added before exception is
// thrown
if (conf != null) errorList.addAll(conf.getErrors());
iter.remove();
logger.warn("Could not configure channel " + channelName
+ " due to: " + e.getMessage(), e); }
} else {
iter.remove();
errorList.add(new FlumeConfigurationError(agentName, channelName,
FlumeConfigurationErrorType.CONFIG_ERROR, ErrorOrWarning.ERROR));
}
}
channelContextMap = newContextMap;
Set<String> tempchannelSet = new HashSet<String>();
tempchannelSet.addAll(channelConfigMap.keySet());
tempchannelSet.addAll(channelContextMap.keySet());
channelSet.retainAll(tempchannelSet);
return channelSet;
}

在validChannels中,根据配置文件中的channel的类型,创建ChannelConfiguration对象,然后插入到AgentConfiguration的configMap中;

到这里,一个完整的FlumeConfiguration对象已经完全加载好了;下面继续;












关于flume配置加载的更多相关文章

  1. 关于flume配置加载(二)

    为什么翻flume的代码,一方面是确实遇到了问题,另一方面是想翻一下flume的源码,看看有什么收获,现在收获还谈不上,因为要继续总结.不够已经够解决问题了,而且确实有好的代码,后续会继续慢慢分享,这 ...

  2. Java实现配置加载机制

    前言 现如今几乎大多数Java应用,例如我们耳熟能详的tomcat, struts2, netty…等等数都数不过来的软件,要满足通用性,都会提供配置文件供使用者定制功能. 甚至有一些例如Netty这 ...

  3. 异常处理之IIS配置加载出错

    问题详情:  一台部署在海外服务器,在管理IIS过程中,出现问题 There was an error when trying to connect. Do you want > to rety ...

  4. 深入理解 Laravel 中 config 配置加载原理

    Laravel的配置加载其实就是加载config目录下所有文件配置.如何过使用php artisan config:cache则会把加载的配置合并到一个配置文件中,下次请求就不会再去加载config目 ...

  5. Springboot学习01- 配置文件加载优先顺序和本地配置加载

    Springboot学习01-配置文件加载优先顺序和本地配置加载 1-项目内部配置文件加载优先顺序 spring boot 启动会扫描以下位置的application.properties或者appl ...

  6. Unity3d通用工具类之数据配置加载类-ini配置文件加载

    Unity3d通用工具类之数据配置加载类-ini配置文件加载 上次我们讲过xml文件的加载配置管理,今天我们换个配置文件,也是比较常见的配置文件.ini格式的数据. 按照国际管理先贴一张啥是.ini文 ...

  7. Unity3d通用工具类之数据配置加载类

    今天,我们来讲讲游戏中的数据配置加载. 什么是游戏数据加载呢?一般来说游戏中会有场景地图. 按照国际惯例,先贴一张游戏场景的地图: 在这张地图上,我们可以看到有很多正六边形,正六边形上有树木.岩石等. ...

  8. 3springboot:springboot配置文件(外部配置加载顺序、自动配置原理,@Conditional)

    1.外部配置加载顺序 SpringBoot也可以从以下位置加载配置: 优先级从高到低 高优先级的配置覆盖低优先级的配置,所有的配置会形成互补配置  1.命令行参数 所有的配置都可以在命令行上进行指定 ...

  9. Expressjs配置加载器

    有些东西就是操刀开干,没什么好解释的.... 问题引入 解决问题 直接上码 env.js index.js 使用方法 初始化 使用方法 写在最后 问题引入 大家都知道在日常的研发过程中,我们的程序会有 ...

随机推荐

  1. linux rlwrap

    无意中发现了rlwrap,终于可以在linux下使用方向键上下翻页输入过的语句了. 比如sqlplus or ggsci中使用. 如果是ubuntu,则在software center中可以直接安装r ...

  2. C++引用的作用和用法

    引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样. 引用的声明方法:类型标识符&引用名=目标变量名: 例如: int q; int &ra=a; 说明: &am ...

  3. 各种url编码

    URLENCODE和URLDECODE是比较常用的URL参数转换方法,为以后使用方便,自己归类一下.   一.JavaScript: 编码:encodeURIComponent(URIString)  ...

  4. 将DataTable导出到Excel

    /// <summary> /// 导出Excel /// </summary> /// <param name="dtData"></p ...

  5. SQL注入截取字符串函数

    在sql注入中,往往会用到截取字符串的问题,例如不回显的情况下进行的注入,也成为盲注,这种情况下往往需要一个一个字符的去猜解,过程中需要用到截取字符串.本文中主要列举三个函数和该函数注入过程中的一些用 ...

  6. Linux内核启动过程start_kernel分析

    虽然题目是start_kernel分析,但是由于我在ubuntu环境下配置实验环境遇到了一些问题,我觉得有必要把这些问题及其解决办法写下来. 首先我使用的是Ubuntu14.04 amx64,以下的步 ...

  7. 远程通知中app更新提示。

    // // AppDelegate.m // SDJK // // Created by Jobs on 6/13/16. // Copyright (c) 2016 com.FlintInfo.dE ...

  8. linux和windows共享文件

    开发板的文档上说可以使用nfs共享文件夹,提供的命令如下: mount –t nfs –o nolock 192.168.1.244:/usr/ /mnt/ 因此我在我的win10上设了共享文件夹,然 ...

  9. Ported my old SPH solver in Unity

    Here is the link to the web player version, http://www-scf.usc.edu/~taian/pages/sph/builds/12212014/ ...

  10. Python数学函数

    1.Python数学函数 1.abs(x):取绝对值,内建函数 2.math.ceil(x):向上取整,在math模块中 3.cmp(x,y):如果 x < y ,返回-1:如果 x == y ...