这里,对Hadoop实现的与升级管理相关的实现类进行分析。通过升级管理器,可以对文件系统的状态进行定时升级更新,保证最良好的工作状态。下面从不同的侧面对与分布式升级相关的内容分类分析。
- 升级命令
与升级命令相关的实现类的继承层次关系如下所示:
- ◦org.apache.hadoop.hdfs.server.protocol.DatanodeCommand(implements org.apache.hadoop.io.Writable)
- ◦org.apache.hadoop.hdfs.server.protocol.UpgradeCommand
1、DatanodeCommand抽象类
该抽象类实现Writable接口,因此是可序列化的,也必须实现该接口中提供的读写方法。DatanodeCommand类内部定义了两个实现该抽象类的命令,如下所示:
- /**
- * Datanode执行注册的命令
- */
- static class Register extends DatanodeCommand {
- private Register() {super(DatanodeProtocol.DNA_REGISTER);}
- public void readFields(DataInput in) {}
- public void write(DataOutput out) {}
- }
- /**
- * Datanode执行清理的命令
- */
- static class Finalize extends DatanodeCommand {
- private Finalize() {super(DatanodeProtocol.DNA_FINALIZE);}
- public void readFields(DataInput in) {}
- public void write(DataOutput out) {}
- }
这两个命令实现类中,对Writeable接口中定义的序列化读写操作并未给出实现,只是一个空动作。
而DatanodeCommand类内部定义了这两各命令作为属性,如下所示:
- public static final DatanodeCommand REGISTER = new Register();
- public static final DatanodeCommand FINALIZE = new Finalize();
因为这两个命令实现类不是public的,所以在获取其实例的时候,是通过WritableFactories工厂工具来加载非public类的Writeable实例,如下所示:
- static {
- WritableFactories.setFactory(Register.class,
- new WritableFactory() {
- public Writable newInstance() {return new Register();}
- });
- WritableFactories.setFactory(Finalize.class,
- new WritableFactory() {
- public Writable newInstance() {return new Finalize();}
- });
- }
该抽象类还定义了一个action代码,表示Datanode需要执行的动作代码,如下所示:
- private int action;
- DatanodeCommand(int action) {
- this.action = action;
- }
- public int getAction() {
- return this.action;
- }
另外,该抽象类应该实现Writeable接口,如下所示:
- public void write(DataOutput out) throws IOException {
- out.writeInt(this.action);
- }
- public void readFields(DataInput in) throws IOException {
- this.action = in.readInt();
- }
其实,就是实现动作代码action支持序列化操作。
2、UpgradeCommand类
该类作为分布式升级的通用命令实现。在升级期间,为了能够获取升级所需要的资源,或者是与其它组件共享某些信息,集群组件会向其它组件发送升级命令。其中,升级命令包含升级的版本号信息,使用它来验证升级组件执行升级的状态。
下面看该类定义的升级命令所要执行的动作代码:
- final static int UC_ACTION_UNKNOWN = DatanodeProtocol.DNA_UNKNOWN; // 未知动作
- public final static int UC_ACTION_REPORT_STATUS = 100; // 报告升级状态
- public final static int UC_ACTION_START_UPGRADE = 101; // 启动升级
该类定义了两个属性:
- private int version; // 版本号
- private short upgradeStatus; // 升级的状态
同样,对现Writeable接口:
- static {
- WritableFactories.setFactory
- (UpgradeCommand.class,
- new WritableFactory() {
- public Writable newInstance() { return new UpgradeCommand(); }
- });
- }
- public void write(DataOutput out) throws IOException {
- super.write(out);
- out.writeInt(this.version);
- out.writeShort(this.upgradeStatus);
- }
- public void readFields(DataInput in) throws IOException {
- super.readFields(in);
- this.version = in.readInt();
- this.upgradeStatus = in.readShort();
- }
支持对两个状态属性的序列化操作。
- 升级状态报告
升级状态报告对应着org.apache.hadoop.hdfs.server.common.UpgradeStatusReport实体类,该类包含了与升级相关的基本状态,包括三个,如下所示:
- protected int version; // 版本号
- protected short upgradeStatus; // 升级的状态
- protected boolean finalized; // 升级后清理工作状态标志
并且,支持对上述属性的序列化操作。
- 分布式升级对象
与分布式升级相关的实体(对象)实现类,实现了org.apache.hadoop.hdfs.server.common.Upgradeable接口,具体的继承层次关系如下所示:
- ◦org.apache.hadoop.hdfs.server.common.UpgradeObject(implements org.apache.hadoop.hdfs.server.common.Upgradeable)
- ◦org.apache.hadoop.hdfs.server.namenode.UpgradeObjectNamenode(implements java.lang.Runnable)
- ◦org.apache.hadoop.hdfs.server.common.UO_Namenode
- ◦org.apache.hadoop.hdfs.server.common.UO_Namenode1
- ◦org.apache.hadoop.hdfs.server.common.UO_Namenode2
- ◦org.apache.hadoop.hdfs.server.common.UO_Namenode3
- ◦org.apache.hadoop.hdfs.server.datanode.UpgradeObjectDatanode(implements java.lang.Runnable)
- ◦org.apache.hadoop.hdfs.server.common.UO_Datanode
- ◦org.apache.hadoop.hdfs.server.common.UO_Datanode1
- ◦org.apache.hadoop.hdfs.server.common.UO_Datanode2
- ◦org.apache.hadoop.hdfs.server.common.UO_Datanode3
下面,对上面给出的相关实现进行阅读分析:
1、Upgradeable接口
该接口是分布式升级对象(distributed upgrade objects)的通用接口,定义了一个分布式对象应该具有的基本行为。每一个分布式升级对象都应该存在一个状态,该状态能够体现该升级对象与某个指定的HDFS的版本相关,因此,使用了一个版本号的变量LAYOUT_VERSION来标识分布式升级对象的版本变化,升级的过程实际上是使该对象的对应的LAYOUT_VERSION为最近(最新)的版本,每一个分布式升级对象都对应一个LAYOUT_VERSION,当执行完成升级以后,会返回最新的LAYOUT_VERSION版本号。
该接口的定义如下所示:
- package org.apache.hadoop.hdfs.server.common;
- import java.io.IOException;
- import org.apache.hadoop.hdfs.server.protocol.UpgradeCommand;
- public interface Upgradeable extends Comparable<Upgradeable> {
- /**
- * 获取升级对象的LAYOUT_VERSION版本号
- */
- int getVersion();
- /**
- * 获取正在执行升级过程的软件组件的类型,实际上只存在两种类型:NAME_NODE与DATA_NODE
- */
- HdfsConstants.NodeType getType();
- /**
- * 升级对象的描述信息
- */
- String getDescription();
- /**
- * 升级状态确定了在一次升级过程中需要完成的升级任务量(占升级总任务量)的百分比
- *
- * 100%意味着升级完成,小于100%则表示没有完成升级。
- * 返回值应该至少包含两个整数值(范围是[0, 100])
- */
- short getUpgradeStatus();
- /**
- * 升级准备
- * 例如,初始化升级数据,初始化升级状态为0
- *
- * 返回一个升级命令,该命令可以向其它的集群组件广播将要执行升级的动作
- * 例如,Namenode通知每个Datanode,需要执行一次分布式升级
- */
- UpgradeCommand startUpgrade() throws IOException;
- /**
- * 完成升级
- * 例如,清除升级数据结构,或将升级元数据写入磁盘
- *
- * 返回一个升级命令,该命令可以向其它的集群组件广播已经完成升级
- * 例如,每个Datanode通知Namenode,它们已经完成升级,而其它的某些Datanode可能孩子升级过程中
- */
- UpgradeCommand completeUpgrade() throws IOException;
- /**
- * 获取升级状态报告
- *
- * @param details 如果指定details=true,表示需要返回详细的升级状态报告
- */
- UpgradeStatusReport getUpgradeStatusReport(boolean details) throws IOException;
- }
上面接口中定义的行为总结如下:
1) LAYOUT_VERSION包含升级对象的所处的版本,也就是根据此版本来确定是否需要升级,以及需要升级到最新的版本;
2)执行升级组件的类型,因为组件不同,它们所处的位置与数据状态时不同的,因此不同的组件执行升级动作是不同的,根据该类型来识别;
3)执行升级过程,应该可以从升级的对象获取到其状态,包括升级完成后的升级报告,通过该状态来掌握集群中升级组件的动态,从而可能需要对不同的升级组件作出合适的决策。
2、UpgradeObject抽象类
该类是升级对象的抽象类,实现了Upgradeable接口。该抽象类主要实现了Upgradeable接口中定义的最基本的方法,并且定义了一个状态变量,如下所示:
- protected short status;
该类实现了获取升级状态报告的方法,如下所示:
- public UpgradeStatusReport getUpgradeStatusReport(boolean details) throws IOException {
- return new UpgradeStatusReport(getVersion(), getUpgradeStatus(), false); // 通过指定版本、状态status、不返回详细升级报告标志status,构造一个UpgradeStatusReport实例返回
- }
可想而知,在准备升级之前,一定需要对版本进行比较,从而决定是否升级,已经升级到哪个版本,如下所示:
- public int compareTo(Upgradeable o) { // 是对一个Upgradeable对象的不同版本号数字进行比较
- if(this.getVersion() != o.getVersion())
- return (getVersion() > o.getVersion() ? -1 : 1);
- int res = this.getType().toString().compareTo(o.getType().toString()); // 升级对象的类型要一致
- if(res != 0)
- return res;
- return getClass().getCanonicalName().compareTo(o.getClass().getCanonicalName());
- }
- public boolean equals(Object o) {
- if (!(o instanceof UpgradeObject)) { // 只支持UpgradeObject类升级对象的比较
- return false;
- }
- return this.compareTo((UpgradeObject)o) == 0;
- }
3、UpgradeObjectNamenode抽象类
该抽象类定义了Namenode类型升级对象的抽象行为。介绍如下:
1)处理升级命令
如下所示:
- /**
- * 处理一个升级命令
- * RPC只具有一个通用的命令,该命令能够使Namenode升级组件与所有升级相关的交互组件进行通信
- *
- * 实际的命令的识别与执行使用该方法来处理,处理完成后仍然返回值升级命令,该返回命令可以在交互的另一端(Datanode)进行分析
- */
- public abstract UpgradeCommand processUpgradeCommand(UpgradeCommand command) throws IOException;
2)广播:启动升级
如下所示:
- public UpgradeCommand startUpgrade() throws IOException {
- return new UpgradeCommand(UpgradeCommand.UC_ACTION_START_UPGRADE, getVersion(), (short)0);
- }
执行该方法,能够向需要升级的Datanode广播将要启动升级的消息。
4、UO_Namenode类
该类实现如下所示:
- class UO_Namenode extends UpgradeObjectNamenode {
- int version; // Namenode升级对象版本号
- UO_Namenode(int v) {
- status = (short)0;
- version = v;
- }
- public int getVersion() {
- return version;
- }
- synchronized public UpgradeCommand processUpgradeCommand(UpgradeCommand command) throws IOException {
- switch(command.getAction()) {
- case UpgradeCommand.UC_ACTION_REPORT_STATUS: // 如果命令是执行报告升级状态
- this.status += command.getCurrentStatus()/8; // 4 reports needed
- break;
- default:
- this.status++;
- }
- return null;
- }
- public UpgradeCommand completeUpgrade() throws IOException {
- return null;
- }
- }
5、UO_Datanode1类
这里拿UO_Namenode1为例,UO_UO_Namenode2、UO_UO_Namenode3也都是基于LAYOUT_VERSION的,如下所示:
- class UO_Namenode1 extends UO_Namenode {
- UO_Namenode1() {
- super(LAYOUT_VERSION+1);
- }
- }
UO_UO_Namenode2、UO_UO_Namenode3分别将初始化的LAYOUT_VERSION+1变成LAYOUT_VERSION+2、LAYOUT_VERSION+3。
- 分布式升级管理器
分布式升级管理器涉及到Namenode与Datanode。下面看升级管理器实现类的继承层次关系:
- ◦org.apache.hadoop.hdfs.server.common.UpgradeManager
- ◦org.apache.hadoop.hdfs.server.namenode.UpgradeManagerNamenode
- ◦org.apache.hadoop.hdfs.server.datanode.UpgradeManagerDatanode
这里,只对UpgradeManager抽象类与UpgradeManagerNamenode实现类进行阅读分析,因为UpgradeManagerDatanode涉及到Datanode进程(org.apache.hadoop.hdfs.server.datanode.DataNode)的实现,这个在后面会详细分析的。
1、UpgradeManager抽象类
该抽象类是通用的升级管理器类。该抽象类定义了如下属性:
- protected SortedSet<Upgradeable> currentUpgrades = null; // 当前升级对象集合
- protected boolean upgradeState = false; // 如果当前正在执行升级过程,其值为true
- protected int upgradeVersion = 0; // 升级版本
- protected UpgradeCommand broadcastCommand = null; // 广播升级命令
该抽象类定义了如下主要操作:
1)获取当前升级对象集合:
- public SortedSet<Upgradeable> getDistributedUpgrades() throws IOException {
- return UpgradeObjectCollection.getDistributedUpgrades(getUpgradeVersion(), getType()); // 根据制定的版本号和升级对象类型获取到分布式升级对象的集合
- }
2)获取升级进度
- public short getUpgradeStatus() {
- if(currentUpgrades == null) // 返回100,表示升级完成(其实是不需要升级)
- return 100;
- return currentUpgrades.first().getUpgradeStatus(); // 获取升级状态
- }
3)初始化升级管理器
- public boolean initializeUpgrade() throws IOException {
- currentUpgrades = getDistributedUpgrades(); // 获取需要升级的分布式升级对象的集合
- if(currentUpgrades == null) {
- setUpgradeState(false, FSConstants.LAYOUT_VERSION); // 不需要升级,设置状态
- return false;
- }
- Upgradeable curUO = currentUpgrades.first(); // 取出集合中第一个升级对象
- setUpgradeState(true, curUO.getVersion()); // 修改状态
- return true;
- }
4)抽象行为
- /**
- * 获取结点类型
- */
- public abstract HdfsConstants.NodeType getType();
- /**
- * 启动升级进程
- */
- public abstract boolean startUpgrade() throws IOException;
- /**
- * 完成升级
- */
- public abstract void completeUpgrade() throws IOException;
2、UpgradeManagerNamenode类
该类是Namenode的升级管理器实现类,当Namenode在安全模式下,将要退出安全模式的时候,会启动分布式升级进程。这时,Namenode直到升级完成才退出安全模式。在升级过程中,Namenode会处理Datanode提交的命令,并更新它的状态。
1)初始化升级管理器
启动分布式升级过程,需要调用startUpgrade方法,如下所示:
- public synchronized boolean startUpgrade() throws IOException {
- if(!upgradeState) { // 当前不是在升级过程中
- initializeUpgrade(); // 初始化升级管理器
- if(!upgradeState) return false; // 初始化失败,返回
- FSNamesystem.getFSNamesystem().getFSImage().writeAll(); // 将启动升级过程中发生的改变写入到fsimage映像中
- }
- assert currentUpgrades != null : "currentUpgrades is null";
- this.broadcastCommand = currentUpgrades.first().startUpgrade(); // 分布式升级对象执行升级前的初始化工作
- NameNode.LOG.info("/n Distributed upgrade for NameNode version "
- + getUpgradeVersion() + " to current LV "
- + FSConstants.LAYOUT_VERSION + " is started.");
- return true;
- }
2)处理升级命令
如下所示:
- synchronized UpgradeCommand processUpgradeCommand(UpgradeCommand command) throws IOException {
- NameNode.LOG.debug("/n Distributed upgrade for NameNode version "
- + getUpgradeVersion() + " to current LV "
- + FSConstants.LAYOUT_VERSION + " is processing upgrade command: "
- + command.getAction() + " status = " + getUpgradeStatus() + "%");
- if(currentUpgrades == null) { // 如果Namenode上没有升级进程启动
- NameNode.LOG.info("Ignoring upgrade command: " + command.getAction() + " version " + command.getVersion() + ". No distributed upgrades are currently running on the NameNode");
- return null;
- }
- UpgradeObjectNamenode curUO = (UpgradeObjectNamenode)currentUpgrades.first(); // 取出分布式升级对象(Namenode上的升级对象)
- if(command.getVersion() != curUO.getVersion()) // 升级命令版本号与升级对象版本号不匹配,不能进行升级
- throw new IncorrectVersionException(command.getVersion(), "UpgradeCommand", curUO.getVersion());
- UpgradeCommand reply = curUO.processUpgradeCommand(command); // 处理一个升级命令,同时返回一个新的升级命令
- if(curUO.getUpgradeStatus() < 100) { // 已经处理完升级命令,执行升级了,但是升级进度还没有达到100%,存在问题
- return reply;
- }
- curUO.completeUpgrade(); // 升级进度返回100,表示完成升级
- NameNode.LOG.info("/n Distributed upgrade for NameNode version "
- + curUO.getVersion() + " to current LV "
- + FSConstants.LAYOUT_VERSION + " is complete.");
- currentUpgrades.remove(curUO); // 删除升级集合中处理完成的该升级对象,准备对下一个执行升级操作
- if(currentUpgrades.isEmpty()) { // 所有升级对象都已经完成升级
- completeUpgrade(); // 调用该类的成员方法,完成升级
- } else { // 对升级对象集合中下一个待升级对象进行处理
- curUO = (UpgradeObjectNamenode)currentUpgrades.first();
- this.broadcastCommand = curUO.startUpgrade(); // 启动
- }
- return reply;
- }
3) 完成升级
如下所示:
- public synchronized void completeUpgrade() throws IOException {
- setUpgradeState(false, FSConstants.LAYOUT_VERSION);
- FSNamesystem.getFSNamesystem().getFSImage().writeAll(); // 将升级状态改变写入磁盘
- currentUpgrades = null;
- broadcastCommand = null;
- FSNamesystem.getFSNamesystem().leaveSafeMode(false); // Namenode已经完成本次升级,退出安全模式
- }
4)分布式升级
如下所示:
- UpgradeStatusReport distributedUpgradeProgress(UpgradeAction action) throws IOException {
- boolean isFinalized = false; // 设置升级后的完成清理标志
- if(currentUpgrades == null) { // 不需要升级
- FSImage fsimage = FSNamesystem.getFSNamesystem().getFSImage();
- isFinalized = fsimage.isUpgradeFinalized(); // 从fsimage映像获取到是否执行完成了清理过程的标志
- if(isFinalized) // 升级后清理工作完成
- return null; // 不需要报告
- return new UpgradeStatusReport(fsimage.getLayoutVersion(), (short)101, isFinalized); // 否则,需要返回清理工作完成报告信息
- }
- UpgradeObjectNamenode curUO = (UpgradeObjectNamenode)currentUpgrades.first(); // 取出Namenode上的升级对象
- boolean details = false; // 不返回详细升级报告信息
- switch(action) { // 根据指定的分布式升级动作,作出相应的处理
- case GET_STATUS:
- break;
- case DETAILED_STATUS:
- details = true;
- break;
- case FORCE_PROCEED:
- curUO.forceProceed();
- }
- return curUO.getUpgradeStatusReport(details); // 返回升级状态报告
- }
对UpgradeManagerNamenode类的分析做个总结:
该类实现了,对在Namenode上运行的分布式升级对象执行升级操作的管理。Namenode上的升级进程执行的时机是在安全模式下,更具体地说,是在Namenode进程就要退出安全模式的时刻执行升级工作的,只有等到升级过程中完成以后,Namenode才退出安全模式。经过升级HDFS集群中,由Namenode升级管理器管理的升级对象的状态都会升级到当前最新的状态,当然不排除提交的升级命令中的状态与实际分布式升级对象状态不匹配的情况,这种情况不会执行升级动作的。