Hadoop集群中,不同进程之间通信需要使用合适的协议才能够进行交互,之前对Hadoop给出的协议接口做了分析。在协议接口中约定了通信双方的特定行为,那么,在实现这些通信协议的实现类中,就能看到指定进程是如何实现协议接口中约定的行为的。这里,阅读分析org.apache.hadoop.hdfs.server.namenode.Namenode实现类。
首先,看一下Namenode类实现的接口,下面是该类声明:
- public class NameNode implements ClientProtocol, DatanodeProtocol, NamenodeProtocol, FSConstants, RefreshAuthorizationPolicyProtocol;
可以看到,Namenode主要实现了ClientProtocol,DatanodeProtocol,NamenodeProtocol这三个用来通信的协议。其中,RefreshAuthorizationPolicyProtocol接口是定义所使用的认证策略,并能根据不同的应用场景来自动刷新其级别,以适应实际应用的需要。
Namenode是HDFS集群中的中心服务器,对于服务器的配置选项,可以通过加载配置文件来进行配置,所以该类中有如下加载配置资源的两行代码:
- static{
- Configuration.addDefaultResource("hdfs-default.xml"); // 默认配置文件
- Configuration.addDefaultResource("hdfs-site.xml"); // 用户配置文件
- }
Namenode类中定义属性如下:
- public static final int DEFAULT_PORT = 8020; // 默认端口号
- public FSNamesystem namesystem; // 用来维护与Datanode相关的重要列表
- private Server server; // RPC服务器
- private InetSocketAddress serverAddress = null; // RPC服务器地址
- private HttpServer httpServer; // 内嵌的HTTP服务器,用来应答到来的HTTP请求
- private InetSocketAddress httpAddress = null; // HTTP服务器地址
- private Thread emptier; // 回收站清理线程
- private boolean stopRequested = false; // 是否阻止请求
- private boolean serviceAuthEnabled = false; // 是否启动服务认证级别
- static NameNodeMetrics myMetrics; // 用来维护Namenode服务器活动状况的统计数据实体
对上面几个重要的属性简单说明一下。
FSNamesystem也是org.apache.hadoop.hdfs.server.namenode包中的类,在Namenode类中是核心的、最重要的。该类主要的功能是对Datanode结点的一些状态等进行登记,便于Namenode进程能够快速获取到指定的Datanode结点的状态等的详细信息,以便进行任务的调度与分配。它主要跟踪如下几个重要的数据表:
1)文件系统到块的映射表(有效文件系统名字-->块列表);
2)全部有效块的列表;
3)块到主机的映射表(块-->主机列表);
4)主机到块的映射表(主机-->块列表);
5)LRU缓存(已经更新心跳状态的主机都放到LRU Cache中)。
HttpServer在org.apache.hadoop.http包中,提供了将Jetty服务器内置于Namenode服务器中,以便可以通过HTTP请求的方式来获取当前Namenode提供服务的信息。
Namenode提供了一个执行初始化的方法,如下所示:
- /**
- * 初始化Namenode
- */
- private void initialize(Configuration conf) throws IOException {
- InetSocketAddress socAddr = NameNode.getAddress(conf); // 从配置conf中获取到Namenode服务器所使用的Socket地址
- int handlerCount = conf.getInt("dfs.namenode.handler.count", 10); // 服务器上处理器Handler线程的数量
- if (serviceAuthEnabled = conf.getBoolean(erviceAuthorizationManager.SERVICE_AUTHORIZATION_CONFIG, false)) {
- PolicyProvider policyProvider =
- (PolicyProvider)(ReflectionUtils.newInstance(conf.getClass(PolicyProvider.POLICY_PROVIDER_CONFIG, HDFSPolicyProvider.class, PolicyProvider.class), conf));
- SecurityUtil.setPolicy(new ConfiguredPolicy(conf, policyProvider)); // 设置安全策略
- }
- this.server = RPC.getServer(this, socAddr.getHostName(), socAddr.getPort(), handlerCount, false, conf); // 创建RPC服务器
- this.serverAddress = this.server.getListenerAddress();
- FileSystem.setDefaultUri(conf, getUri(serverAddress));
- LOG.info("Namenode up at: " + this.serverAddress);
- myMetrics = new NameNodeMetrics(conf, this); // 初始化NameNodeMetrics
- this.namesystem = new FSNamesystem(this, conf);
- startHttpServer(conf);
- this.server.start(); // 启动RPC服务器
- startTrashEmptier(conf);
- }
在Namenode类实现中,其中实现的操作基本上都是由FSNamesystem来完成的。在这里,我们先不关心Namenode具体是如何实现那些基本操作的,而只是关注Namenode的功能特性,在后面再对FSNamesystem类进行分析。这里使用方式就是,根据Namenode所实现的接口中定义的操作,来分析Namenode服务器所具备的基本功能,或者说提供的基本服务。
NamenodeProtocol实现
在Namenode类中,实现了NamenodeProtocol协议接口中定义的getBlocks方法,如下所示:
- /**
- * 返回指定大小为size的Datanode上块及其所在位置列表
- */
- public BlocksWithLocations getBlocks(DatanodeInfo datanode, long size) throws IOException {
- if(size <= 0) {
- throw new IllegalArgumentException("Unexpected not positive size: "+size);
- }
- return namesystem.getBlocks(datanode, size); // 调用namesystem的getBlocks方法来真正实现
- }
给定DatanodeInfo datanode,它是一个描述Datanode的状态的实体类对象,通过getBlocks方法,可以获取到总的块大小为size的BlocksWithLocations,即描述这些块的位置信息的BlocksWithLocations对象。
ClientProtocol实现
实现ClientProtocol协议接口中定义的方法,如下所示:
- /**
- * 获取到指定文件src的全部块的信息
- *
- * @param src 指定的文件
- * @param offset 起始偏移位置
- * @param length 长度
- */
- public LocatedBlocks getBlockLocations(String src, long offset, long length) throws IOException {
- myMetrics.numGetBlockLocations.inc();
- return namesystem.getBlockLocations(getClientMachine(), src, offset, length); // 调用:获取到指定文件的全部块信息
- }
- /**
- * 在制定的文件系统命名空间中创建一个文件入口(entry)
- */
- public void create(String src, FsPermission masked, String clientName, boolean overwrite, short replication, long blockSize) throws IOException {
- String clientMachine = getClientMachine();
- if (stateChangeLog.isDebugEnabled()) {
- stateChangeLog.debug("*DIR* NameNode.create: file " +src+" for "+clientName+" at "+clientMachine);
- }
- if (!checkPathLength(src)) {
- throw new IOException("create: Pathname too long. Limit " + MAX_PATH_LENGTH + " characters, " + MAX_PATH_DEPTH + " levels.");
- }
- namesystem.startFile(src,
- new PermissionStatus(UserGroupInformation.getCurrentUGI().getUserName(),
- null, masked),
- clientName, clientMachine, overwrite, replication, blockSize); // 调用:执行文件的创建操作
- myMetrics.numFilesCreated.inc();
- myMetrics.numCreateFileOps.inc();
- }
- /**
- * 对指定文件执行追加写操作
- */
- public LocatedBlock append(String src, String clientName) throws IOException {
- String clientMachine = getClientMachine();
- if (stateChangeLog.isDebugEnabled()) {
- stateChangeLog.debug("*DIR* NameNode.append: file " +src+" for "+clientName+" at "+clientMachine);
- }
- LocatedBlock info = namesystem.appendFile(src, clientName, clientMachine); // 调用:执行文件的追加写操作
- myMetrics.numFilesAppended.inc();
- return info;
- }
- /**
- * 设置副本因子
- */
- public boolean setReplication(String src, short replication) throws IOException {
- return namesystem.setReplication(src, replication); // 调用:设置副本因子
- }
- /**
- * 设置权限
- */
- public void setPermission(String src, FsPermission permissions) throws IOException {
- namesystem.setPermission(src, permissions); // 调用:设置权限
- }
- /**
- * 设置文件属主
- */
- public void setOwner(String src, String username, String groupname) throws IOException {
- namesystem.setOwner(src, username, groupname); // 调用:设置文件属主
- }
- /**
- * 向指定的文件中写入数据块
- */
- public LocatedBlock addBlock(String src, String clientName) throws IOException {
- stateChangeLog.debug("*BLOCK* NameNode.addBlock: file " +src+" for "+clientName);
- LocatedBlock locatedBlock = namesystem.getAdditionalBlock(src, clientName); // 调用:获取并执行写入操作
- if (locatedBlock != null)
- myMetrics.numAddBlockOps.inc();
- return locatedBlock;
- }
- /**
- * 客户端放弃对指定块的操作
- */
- public void abandonBlock(Block b, String src, String holder) throws IOException {
- stateChangeLog.debug("*BLOCK* NameNode.abandonBlock: " +b+" of file "+src);
- if (!namesystem.abandonBlock(b, src, holder)) { // 调用
- throw new IOException("Cannot abandon block during write to " + src);
- }
- }
- /**
- * 客户端完成对指定文件的写操作
- */
- public boolean complete(String src, String clientName) throws IOException {
- stateChangeLog.debug("*DIR* NameNode.complete: " + src + " for " + clientName);
- CompleteFileStatus returnCode = namesystem.completeFile(src, clientName); // 调用:执行写操作,写完成之后关闭该文件流
- if (returnCode == CompleteFileStatus.STILL_WAITING) {
- return false;
- } else if (returnCode == CompleteFileStatus.COMPLETE_SUCCESS) {
- return true;
- } else {
- throw new IOException("Could not complete write to file " + src + " by " + clientName);
- }
- }
- /**
- * 客户端向Namenode报告corrupted块的信息
- */
- public void reportBadBlocks(LocatedBlock[] blocks) throws IOException {
- stateChangeLog.info("*DIR* NameNode.reportBadBlocks");
- for (int i = 0; i < blocks.length; i++) {
- Block blk = blocks[i].getBlock();
- DatanodeInfo[] nodes = blocks[i].getLocations();
- for (int j = 0; j < nodes.length; j++) {
- DatanodeInfo dn = nodes[j];
- namesystem.markBlockAsCorrupt(blk, dn); // 调用:标记属于该Datanode的corrupted状态的块
- }
- }
- }
- /**
- * 重命名文件
- */
- public boolean rename(String src, String dst) throws IOException {
- stateChangeLog.debug("*DIR* NameNode.rename: " + src + " to " + dst);
- if (!checkPathLength(dst)) {
- throw new IOException("rename: Pathname too long. Limit " + MAX_PATH_LENGTH + " characters, " + MAX_PATH_DEPTH + " levels.");
- }
- boolean ret = namesystem.renameTo(src, dst); // 调用:重命名文件
- if (ret) {
- myMetrics.numFilesRenamed.inc();
- }
- return ret;
- }
- /**
- * 删除文件
- */
- public boolean delete(String src, boolean recursive) throws IOException {
- if (stateChangeLog.isDebugEnabled()) {
- stateChangeLog.debug("*DIR* Namenode.delete: src=" + src + ", recursive=" + recursive);
- }
- boolean ret = namesystem.delete(src, recursive); // 调用:删除文件
- if (ret)
- myMetrics.numDeleteFileOps.inc();
- return ret;
- }
- /**
- * 管理客户端状态,如果Namenode在一段时间内接收不到客户端心跳状态,标记为挂掉;经过周期检测,又获取到对应客户端心跳状态,为其解锁。
- */
- public void renewLease(String clientName) throws IOException {
- namesystem.renewLease(clientName); // 调用
- }
- /**
- * 获取指定目录下文件列表
- */
- public FileStatus[] getListing(String src) throws IOException {
- FileStatus[] files = namesystem.getListing(src); // 调用
- if (files != null) {
- myMetrics.numGetListingOps.inc();
- }
- return files;
- }
- /**
- * 获取指定文件的状态信息
- */
- public FileStatus getFileInfo(String src) throws IOException {
- return namesystem.getFileInfo(src); // 调用
- }
- /**
- * 获取文件系统的统计信息
- */
- public long[] getStats() throws IOException {
- return namesystem.getStats(); // 调用
- }
- /**
- * 创建目录
- */
- public boolean mkdirs(String src, FsPermission masked) throws IOException {
- stateChangeLog.debug("*DIR* NameNode.mkdirs: " + src);
- if (!checkPathLength(src)) {
- throw new IOException("mkdirs: Pathname too long. Limit " + MAX_PATH_LENGTH + " characters, " + MAX_PATH_DEPTH + " levels.");
- }
- return namesystem.mkdirs(src,
- new PermissionStatus(UserGroupInformation.getCurrentUGI().getUserName(),
- null, masked)); // 调用
- }
- /**
- * 设置安全模式
- */
- public boolean setSafeMode(SafeModeAction action) throws IOException {
- return namesystem.setSafeMode(action); // 调用
- }
- /**
- * 保存命名空间映像
- */
- public void saveNamespace() throws IOException {
- namesystem.saveNamespace(); // 调用
- }
- /**
- * 刷新Datanode结点的列表
- */
- public void refreshNodes() throws IOException {
- namesystem.refreshNodes(new Configuration()); // 调用
- }
- /**
- * 完成升级操作(删除在升级期间保存的文件系统的状态,一旦执行该方法,改变将不可逆转)
- */
- public void finalizeUpgrade() throws IOException {
- namesystem.finalizeUpgrade(); // 调用
- }
- /**
- * 报告升级进度
- */
- public UpgradeStatusReport distributedUpgradeProgress(UpgradeAction action) throws IOException {
- return namesystem.distributedUpgradeProgress(action); // 调用
- }
- /**
- * 保存Namenode的状态信息,写入到指定的文件中
- */
- public void metaSave(String filename) throws IOException {
- namesystem.metaSave(filename); // 调用
- }
- /**
- * 设置指定文件或目录的内容摘要信息
- */
- public ContentSummary getContentSummary(String path) throws IOException {
- return namesystem.getContentSummary(path); // 调用
- }
- /**
- * 设置指定目录的配额
- */
- public void setQuota(String path, long namespaceQuota, long diskspaceQuota) throws IOException {
- namesystem.setQuota(path, namespaceQuota, diskspaceQuota); // 调用
- }
- /**
- * 将指定文件的元数据写入到持久存储中
- */
- public void fsync(String src, String clientName) throws IOException {
- namesystem.fsync(src, clientName); // 调用
- }
- /**
- * 设置文件修改时间
- */
- public void setTimes(String src, long mtime, long atime) throws IOException {
- namesystem.setTimes(src, mtime, atime); // 调用:设置文件修改时间
- }
DatanodeProtocol实现
Namenode需要与Datanode进行通信,所以必须实现与DatanodeProtocol协议接口。
下面是对DatanodeProtocol协议接口中定义的基本操作的实现:
- /**
- * Datanode向Namenode注册,返回Namenode需要验证的有关Datanode的信息DatanodeRegistration的实例
- */
- public DatanodeRegistration register(DatanodeRegistration nodeReg) throws IOException {
- verifyVersion(nodeReg.getVersion()); // 验证协议版本
- namesystem.registerDatanode(nodeReg); // 调用:注册指定的Datanode结点
- return nodeReg;
- }
- /**
- * Datanode向Namenode发送心跳状态报告,显示其当前状态
- */
- public DatanodeCommand[] sendHeartbeat(DatanodeRegistration nodeReg,
- long capacity,
- long dfsUsed,
- long remaining,
- int xmitsInProgress,
- int xceiverCount) throws IOException {
- verifyRequest(nodeReg);
- return namesystem.handleHeartbeat(nodeReg, capacity, dfsUsed, remaining, xceiverCount, xmitsInProgress); // 调用:处理Datanode发送的心跳信息
- }
- /**
- * 指定Datanode向Namenode报告其上的块状态报告
- */
- public DatanodeCommand blockReport(DatanodeRegistration nodeReg, long[] blocks) throws IOException {
- verifyRequest(nodeReg); // 验证Datanode向Namenode请求的合法性
- BlockListAsLongs blist = new BlockListAsLongs(blocks);
- stateChangeLog.debug("*BLOCK* NameNode.blockReport: " +"from "+nodeReg.getName()+" "+blist.getNumberOfBlocks() +" blocks");
- namesystem.processReport(nodeReg, blist); // 调用:处理块状态报告
- if (getFSImage().isUpgradeFinalized())
- return DatanodeCommand.FINALIZE;
- return null;
- }
- /**
- * Datanode向Namenode报告最近接收到的数据块,以及对数据块的操作信息
- */
- public void blockReceived(DatanodeRegistration nodeReg, Block blocks[], String delHints[]) throws IOException {
- verifyRequest(nodeReg); // 验证请求是否合法
- stateChangeLog.debug("*BLOCK* NameNode.blockReceived: " +"from "+nodeReg.getName()+" "+blocks.length+" blocks.");
- for (int i = 0; i < blocks.length; i++) {
- namesystem.blockReceived(nodeReg, blocks[i], delHints[i]); // 调用
- }
- }
- /**
- * 报告错误信息
- */
- public void errorReport(DatanodeRegistration nodeReg, int errorCode, String msg) throws IOException {
- String dnName = (nodeReg == null ? "unknown DataNode" : nodeReg.getName());
- LOG.info("Error report from " + dnName + ": " + msg); // 将来自指定的Datanode的出错信息登录到日志文件中
- if (errorCode == DatanodeProtocol.NOTIFY) {
- return;
- }
- verifyRequest(nodeReg);
- if (errorCode == DatanodeProtocol.DISK_ERROR) { // 如果是对应的Datanode出现了磁盘错误
- namesystem.removeDatanode(nodeReg); // 调用:删除该Datanode的描述信息
- }
- }
- /**
- * 在分布式系统升级过程中,向Namenode发送命令
- */
- public UpgradeCommand processUpgradeCommand(UpgradeCommand comm) throws IOException {
- return namesystem.processDistributedUpgradeCommand(comm); // 调用
- }
其它实现
用于Namenode与客户端通信,还定义了如下几个与Namenode管理文件系统命名空间相关的操作,主要是对EditLog和FsImage文件的操作:
- /**
- * 当前EditLog日志文件的大小
- */
- public long getEditLogSize() throws IOException {
- return namesystem.getEditLogSize(); // 调用
- }
- /**
- * 回滚EditLog文件
- */
- public CheckpointSignature rollEditLog() throws IOException {
- return namesystem.rollEditLog(); // 调用
- }
- /**
- * 回滚FsImage映像文件
- */
- public void rollFsImage() throws IOException {
- namesystem.rollFSImage(); // 调用
- }
- /**
- * 获取FsImage映像文件名称
- */
- public File getFsImageName() throws IOException {
- return getFSImage().getFsImageName();
- }
- /**
- * 获取FsImage映像文件
- */
- public FSImage getFSImage() {
- return namesystem.dir.fsImage;
- }
- /**
- * 获取到FsImage映像文件的所有检查点
- */
- public File[] getFsImageNameCheckpoint() throws IOException {
- return getFSImage().getFsImageNameCheckpoint();
- }
在Namenode上,对文件系统名字空间进行的任何操作,Hadoop采用记录事务日志的方式来保存这些操作的记录,对应的事务日志文件就是EditLog文件,该文件存放在磁盘上。而FsImage映像文件,是Hadoop集群工作的过程中使用的,便于Namenode进程管理文件系统的命名空间。当Namenode启动的时候,会首先读取EditLog与FsImage文件,并将EditLog作用于FsImage,因为此时FsImage文件一定是最新的事务日志记录文件的映像,并将EditLog文件删除(因为它可能会在Hadoop集群工作过程中变成旧的文件,也就是不是最新的事务记录);在Namenode启动之后工作的过程中,仍然会有对文件系统命名空间执行操作的事务记录,这些记录都被保存到一个新的EditLog事务日志文件中,以便下次启动Namenode的时候,将最新的事务日志记录作用于FsImage上。
另外,Namenode类中实现了创建一个Namenode实例的方法createNameNode,该方法是static的,通过命令行参数一个Hadoop配置类实例来创建一个Namenode实例,方法实现如下所示:
- public static NameNode createNameNode(String argv[], Configuration conf) throws IOException {
- if (conf == null)
- conf = new Configuration(); // 如果指定的Configuration类实例为null,创一个默认的配置类实例
- StartupOption startOpt = parseArguments(argv); // 解析命令行获取到Namenode启动参数
- if (startOpt == null) {
- printUsage(); // 如果解析命令行参数返回null,打印命令用法信息
- return null;
- }
- setStartupOption(conf, startOpt); // 设置Namenode启动选项
- switch (startOpt) { // 根据设置的参数选项,进行处理
- case FORMAT:
- boolean aborted = format(conf, true);
- System.exit(aborted ? 1 : 0);
- case FINALIZE:
- aborted = finalize(conf, true);
- System.exit(aborted ? 1 : 0);
- default:
- }
- NameNode namenode = new NameNode(conf); // 构造一个Namenode实例
- return namenode;
- }
关于配置启动选项,可以查看org.apache.hadoop.hdfs.server.common.HdfsConstants接口中定义了枚举类StartupOption,如下所示:
- static public enum StartupOption{
- FORMAT ("-format"), // 格式化HDFS选项
- REGULAR ("-regular"), // 正常启动选项
- UPGRADE ("-upgrade"), // 分布式升级选项
- ROLLBACK("-rollback"), // 事务回滚选项
- FINALIZE("-finalize"), // 确认升级完成选项
- IMPORT ("-importCheckpoint"); //导入检查点选项
- private String name = null;
- private StartupOption(String arg) {this.name = arg;}
- public String getName() {return name;}
- }
上面创建一个Namenode,在解析完命令行参数后,得到一个枚举类实例,然后根据它来设置Namenode启动选项,如下所示:
- private static void setStartupOption(Configuration conf, StartupOption opt) {
- conf.set("dfs.namenode.startup", opt.toString()); // 实际上,将命令行解析得到的启动选项,设置到一个Confirmation配置实例上
- }
接着,在构造Namenode实例之前,根据设置的附加启动选项,做一个预处理(格式化或某次升级之后的确认动作),如果是格式化操作,具体操作为:
- /**
- * 验证配置的目录是否存在,如果验证合法则执行格式化操作
- */
- private static boolean format(Configuration conf, boolean isConfirmationNeeded) throws IOException {
- Collection<File> dirsToFormat = FSNamesystem.getNamespaceDirs(conf); // 通过配置实例conf获取到需要进行格式化的文件系统命名空间目录
- Collection<File> editDirsToFormat = FSNamesystem.getNamespaceEditsDirs(conf); // 获取到需要进行格式化的文件系统命名空间修改目录
- for(Iterator<File> it = dirsToFormat.iterator(); it.hasNext();) { // 验证上述目录的合法性
- File curDir = it.next();
- if (!curDir.exists())
- continue;
- if (isConfirmationNeeded) {
- System.err.print("Re-format filesystem in " + curDir +" ? (Y or N) ");
- if (!(System.in.read() == 'Y')) {
- System.err.println("Format aborted in "+ curDir);
- return true; // 放弃对文件系统命名空间的格式化操作
- }
- while(System.in.read() != '/n'); // discard the enter-key
- }
- }
- FSNamesystem nsys = new FSNamesystem(new FSImage(dirsToFormat, editDirsToFormat), conf); // 基于构造的FsImage文件实例来创建文件系统命名空间
- nsys.dir.fsImage.format(); // 对FsImage文件进行格式化
- return false;
- }
关于FsImage与EditLog在文件系统中的所在位置的选择,在org.apache.hadoop.hdfs.server.namenode.FSNamesystem类中可以看到详细的情况。
如果是确认升级完成命令行,则实现如下所示:
- private static boolean finalize(Configuration conf, boolean isConfirmationNeeded) throws IOException {
- Collection<File> dirsToFormat = FSNamesystem.getNamespaceDirs(conf); // 文件系统命名空间文件FsImage所在目录
- Collection<File> editDirsToFormat = FSNamesystem.getNamespaceEditsDirs(conf); // EditLog所在目录
- FSNamesystem nsys = new FSNamesystem(new FSImage(dirsToFormat, editDirsToFormat), conf); // 构造FSNamesystem
- System.err.print(
- "/"finalize/" will remove the previous state of the files system./n"
- + "Recent upgrade will become permanent./n"
- + "Rollback option will not be available anymore./n");
- if (isConfirmationNeeded) {
- System.err.print("Finalize filesystem state ? (Y or N) ");
- if (!(System.in.read() == 'Y')) {
- System.err.println("Finalize aborted.");
- return true; // 放弃升级确认
- }
- while(System.in.read() != '/n'); // discard the enter-key
- }
- nsys.dir.fsImage.finalizeUpgrade(); // 调用FSImage类的finalizeUpgrade方法实现升级完成的确认
- return false;
- }
关于FsImage文件的操作,可以查看org.apache.hadoop.hdfs.server.namenode.FSImage类的具体实现。在后面会专门对FSImage类进行详细阅读分析的。
下面,对Namenode类的实现做一个总结:
从Namenode实现类来看,它主要是实现了Namenode服务器进程与Datanode进程、文件系统客户端进程,以及Secondary NameNode进程进行交互过程中一些重要的基本的操作,具体表现为,Namenode实现了ClientProtocol协议来与客户端交互,实现了DatanodeProtocol协议来与Datanode进行交互,实现了NamenodeProtocol协议来与Secondary NameNode进行交互。而且,该类还给出了一个static方法,通过命令行的方式用来构造一个Namenode实例,并启动Namenode服务器进程。