Hadoop源码分析(6)

1、 hadoop配置文件处理

在(5)中分析了namenode的启动,在创建namenode对象前,会先创建一个HdfsConfiguration对象。其代表了hadoop对配置文件的处理。

(5)中分析到了HdfsConfiguration和其父类Configuration都有一段静态代码块。在HdfsConfiguration的静态代码块中会有一个addDeprecatedKeys方法,这个方法会调用其父类的静态方法addDeprecations,并创建一个包含数据的DeprecationDelta数组,作为该方法的参数。这段代码的主要作用是为了解决hadoop的旧版本弃用的配置和新版本的配置的问题。
创建的DeprecationDelta的初始化参数如下:

public DeprecationDelta(String key, String newKey) {
      this(key, new String[] { newKey }, null);
    }
   
   DeprecationDelta(String key, String[] newKeys, String customMessage) {
      Preconditions.checkNotNull(key);
      Preconditions.checkNotNull(newKeys);
      Preconditions.checkArgument(newKeys.length > 0);
      this.key = key;
      this.newKeys = newKeys;
      this.customMessage = customMessage;
    }

这个方法需要传入两个参数,分别是弃用key和新的key。然后调用一个重载方法(第5行),这个方法主要就是将传入的值保存为其内部的属性。
接着继续分析addDeprecatedKeys方法,其内容如下:

  public static void addDeprecations(DeprecationDelta[] deltas) {
    DeprecationContext prev, next;
    do {
      prev = deprecationContext.get();
      next = new DeprecationContext(prev, deltas);
    } while (!deprecationContext.compareAndSet(prev, next));
  }

首先是第4行,出现了一个参数deprecationContext,其定义如下:

/**
   * The global DeprecationContext.
   */
  private static AtomicReference<DeprecationContext> deprecationContext =
      new AtomicReference<DeprecationContext>(
          new DeprecationContext(null, defaultDeprecations));

由上述定义可以看出其类型为AtomicReference,这是一个用于保证多线程下数据原子性的类,其重点在其实例化时传入的DeprecationContext类。在addDeprecations中的第4行调用的get方法获取的便是这个对象。同时第5行也会重新创建一个DeprecationContext对象,然后设置到deprecationContext中。DeprecationContext的创建方法如下:

@SuppressWarnings("unchecked")
    DeprecationContext(DeprecationContext other, DeprecationDelta[] deltas) {
      HashMap<String, DeprecatedKeyInfo> newDeprecatedKeyMap = 
        new HashMap<String, DeprecatedKeyInfo>();
      HashMap<String, String> newReverseDeprecatedKeyMap =
        new HashMap<String, String>();
      if (other != null) {
        for (Entry<String, DeprecatedKeyInfo> entry :
            other.deprecatedKeyMap.entrySet()) {
          newDeprecatedKeyMap.put(entry.getKey(), entry.getValue());
        }
        for (Entry<String, String> entry :
            other.reverseDeprecatedKeyMap.entrySet()) {
          newReverseDeprecatedKeyMap.put(entry.getKey(), entry.getValue());
        }
      }
      for (DeprecationDelta delta : deltas) {
        if (!newDeprecatedKeyMap.containsKey(delta.getKey())) {
          DeprecatedKeyInfo newKeyInfo =
            new DeprecatedKeyInfo(delta.getNewKeys(), delta.getCustomMessage());
          newDeprecatedKeyMap.put(delta.key, newKeyInfo);
          for (String newKey : delta.getNewKeys()) {
            newReverseDeprecatedKeyMap.put(newKey, delta.key);
          }
        }
      }
      this.deprecatedKeyMap =
        UnmodifiableMap.decorate(newDeprecatedKeyMap);
      this.reverseDeprecatedKeyMap =
        UnmodifiableMap.decorate(newReverseDeprecatedKeyMap);
    }

这段代码很简单。首先是第3行到第6行,这里新建了两个hashmap;然后是第7行到16行,这里在处理传入的第一个参数(other),从上文可以看出这个other代表着先前存储的数据,这里的处理也很简单,就是从其内部存储的两个hashmap(deprecatedKeyMap和reverseDeprecatedKeyMap)中取出数据,存入先前创建的hashmap中;然后是第17行到第26行,这里在处理第二个参数deltas,首先使用for循环遍历该数组,然后判断其是否在newDeprecatedKeyMap中,这里判断的用的数据是DeprecationDelta类的getKey方法,这个方法返回的是其存储的废弃的key,如果废弃的key不在该map中,则首先利用DeprecationDelta存储的新key和相关信息创建一个DeprecatedKeyInfo对象,然后将废弃的key和这个DeprecatedKeyInfo对象存储到上述map中,最后再遍历DeprecationDelta中的新key,将新key和旧key存储到newReverseDeprecatedKeyMap中;最是第27行到末尾将上述创建的两个map赋值给其自身创建的属性。

至此,对于弃用key的处理便解析完成了。简单来说,hadoop会将弃用key和新key存入到DeprecationContext类中,而这个类实际是用两个hashmap来存储这些key,其中一个map是用弃用的key为map的key,用新key的信息作为value;另一个map是用新key作为map的key,弃用key作为map的value。

解析完弃用key的处理后,HdfsConfiguration对象的初始化便完成了。但是在初始化的过程中,其并没有加载配置文件中的配置。这是由于hadoop将加载配置文件的操作放在其使用的时候。HdfsConfiguration的使用主要有set和get方法。set方法是将配置信息存储到configuration中,其代码如下:

public void set(String name, String value) {
    set(name, value, null);
  }
  
   public void set(String name, String value, String source) {
    Preconditions.checkArgument(
        name != null,
        "Property name must not be null");
    Preconditions.checkArgument(
        value != null,
        "The value of property " + name + " must not be null");
    name = name.trim();
    DeprecationContext deprecations = deprecationContext.get();
    if (deprecations.getDeprecatedKeyMap().isEmpty()) {
      getProps();
    }
    getOverlay().setProperty(name, value);
    getProps().setProperty(name, value);
    String newSource = (source == null ? "programatically" : source);

    if (!isDeprecated(name)) {
      updatingResource.put(name, new String[] {newSource});
      String[] altNames = getAlternativeNames(name);
      if(altNames != null) {
        for(String n: altNames) {
          if(!n.equals(name)) {
            getOverlay().setProperty(n, value);
            getProps().setProperty(n, value);
            updatingResource.put(n, new String[] {newSource});
          }
        }
      }
    }
    else {
      String[] names = handleDeprecation(deprecationContext.get(), name);
      String altSource = "because " + name + " is deprecated";
      for(String n : names) {
        getOverlay().setProperty(n, value);
        getProps().setProperty(n, value);
        updatingResource.put(n, new String[] {altSource});
      }
    }
  } 

这里的set有两个,但实际调用的都是第5行的这个set方法。这个方法可以分为三个部分,首先是第6行到16行,这里主要是在做参数处理;然后是第17、18行,这里是将存储设置的配置;最后是19行到末尾,这里在处理弃用key。

第6行到第11行,这里其实只调用了一个方法,这个方法用于检查传入的name与value是否为空,然后是12行去除name前后的空格,然后是第13行调用了deprecationContext的get方法,这个方法在上文解析废弃key的时候提到过,它返回的是用来存储废弃key数据的DeprecationContext对象。然后是第14行的if语句,这里会判断DeprecationContext对象中的map是否为空,若为空则执行getProps方法,这个方法稍后再分析。

然后是第17、18行,这里会调用getProps和getOverlay方法,这两个方法会返回Properties对象,然后调用setProperty方法将数据设置到Properties中。
最后是第21行的if else语句,首先if语句内先调用isDeprecated方法判断设置的name是否弃用的key。这个方法的实现方式如下:

  public static boolean isDeprecated(String key) {
    return deprecationContext.get().getDeprecatedKeyMap().containsKey(key);
  }

这个方法很简单,就是从上文解析的存储弃用key的map中判断是否存在存入的key。第21行的if中对这个方法的结果取反,所以当key不是弃用key的时候会执行if内部的语句。其内部首先是第22行会更新其来源,然后是第23行调用getAlternativeNames方法查询该name的替代名称。这个方法的内容如下:

private String[] getAlternativeNames(String name) {
    String altNames[] = null;
    DeprecatedKeyInfo keyInfo = null;
    DeprecationContext cur = deprecationContext.get();
    String depKey = cur.getReverseDeprecatedKeyMap().get(name);
    if(depKey != null) {
      keyInfo = cur.getDeprecatedKeyMap().get(depKey);
      if(keyInfo.newKeys.length > 0) {
        if(getProps().containsKey(depKey)) {
          //if deprecated key is previously set explicitly
          List<String> list = new ArrayList<String>();
          list.addAll(Arrays.asList(keyInfo.newKeys));
          list.add(depKey);
          altNames = list.toArray(new String[list.size()]);
        }
        else {
          altNames = keyInfo.newKeys;
        }
      }
    }
    return altNames;
  }

这个方法也不复杂。首先是第4行获取处理弃用key的DeprecationContext对象,因为传入的key不是弃用key,所以第5行会从存储新key的map中,拿到该key对应的弃用key。然后第6行当弃用key不为空的时候执行if内的操作。首先是第7行从存储弃用key的map中拿到该key对应的新key信息。这里如此取数据是有原因的,在上文分析过两个map的key与value,在存储新key的map中,新key是map的key,与新key对应的弃用key是map的value,但是在存储弃用key的map中,弃用key是map的key,map的value却不是新key,它是DeprecatedKeyInfo类的对象,它创建时调用的构造方法如下:

    DeprecatedKeyInfo(String[] newKeys, String customMessage) {
      this.newKeys = newKeys;
      this.customMessage = customMessage;
    }

这里可以看见它存储的newkeys是一个数组,即一个弃用key可能会有多个newkey。

然后是getAlternativeNames方法的第8行,这里会判断存储的newkeys的长度是否大于0,大于0则继续向下执行。然后是第9行判断弃用key是否已经在配置文件中存在,若存在则将弃用key与其存储所有新key一起作为替用名称返回否则只讲存储的新key作为替用名称返回。

然后继续看set方法,在第23行获取到了替用名称后,接着24行到33行就是遍历这个数组,将所有替用名称的也设置到配置文件中去。

然后是第34行对弃用key的处理,首先是调用了handleDeprecation方法来处理弃用key的替换名称,然后就和新key一样将替用名称也一起设置到配置文件中。其中handleDeprecation方法的内容如下:

private String[] handleDeprecation(DeprecationContext deprecations,
      String name) {
    if (null != name) {
      name = name.trim();
    }
    ArrayList<String > names = new ArrayList<String>();
	if (isDeprecated(name)) {
      DeprecatedKeyInfo keyInfo = deprecations.getDeprecatedKeyMap().get(name);
      warnOnceIfDeprecated(deprecations, name);
      for (String newKey : keyInfo.newKeys) {
        if(newKey != null) {
          names.add(newKey);
        }
      }
    }
    if(names.size() == 0) {
    	names.add(name);
    }
    for(String n : names) {
	  String deprecatedKey = deprecations.getReverseDeprecatedKeyMap().get(n);
	  if (deprecatedKey != null && !getOverlay().containsKey(n) &&
	      getOverlay().containsKey(deprecatedKey)) {
	    getProps().setProperty(n, getOverlay().getProperty(deprecatedKey));
	    getOverlay().setProperty(n, getOverlay().getProperty(deprecatedKey));
	  }
    }
    return names.toArray(new String[names.size()]);
  }

首先是第3行到第5行,这里是对name进行以下检查判断,然后是第6行创建了一个list,然后是第7行到第15行这里会先判断传入的name是否是弃用key,若是则从存储弃用key的map中取出其存储的newkeys,将这些newkeys作为替换名称添加到数组中。然后是第16行到第18行,若list中没有数据则将其自身添加到list中。最后是第19行到第26行,这会遍历所有的替换名,并在overlay中查看该key与替用key是否已存在,若弃用key存在,替换名不存在,则将替换名存储到配置文件中。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值