Hadoop-0.20.0源代码分析(14)

Hadoop集群中,不同进程之间通信需要使用合适的协议才能够进行交互,之前对Hadoop给出的协议接口做了分析。在协议接口中约定了通信双方的特定行为,那么,在实现这些通信协议的实现类中,就能看到指定进程是如何实现协议接口中约定的行为的。这里,阅读分析org.apache.hadoop.hdfs.server.namenode.Namenode实现类。

首先,看一下Namenode类实现的接口,下面是该类声明:

  1. public class NameNode implements ClientProtocol, DatanodeProtocol, NamenodeProtocol, FSConstants, RefreshAuthorizationPolicyProtocol;  

可以看到,Namenode主要实现了ClientProtocol,DatanodeProtocol,NamenodeProtocol这三个用来通信的协议。其中,RefreshAuthorizationPolicyProtocol接口是定义所使用的认证策略,并能根据不同的应用场景来自动刷新其级别,以适应实际应用的需要。

Namenode是HDFS集群中的中心服务器,对于服务器的配置选项,可以通过加载配置文件来进行配置,所以该类中有如下加载配置资源的两行代码:

  1. static{  
  2.   Configuration.addDefaultResource("hdfs-default.xml"); // 默认配置文件   
  3.   Configuration.addDefaultResource("hdfs-site.xml");    // 用户配置文件   
  4. }  

Namenode类中定义属性如下:

  1. public static final int DEFAULT_PORT = 8020;        // 默认端口号   
  2. public FSNamesystem namesystem;             // 用来维护与Datanode相关的重要列表    
  3. private Server server;                  // RPC服务器   
  4. private InetSocketAddress serverAddress = null;         // RPC服务器地址   
  5. private HttpServer httpServer;              // 内嵌的HTTP服务器,用来应答到来的HTTP请求   
  6. private InetSocketAddress httpAddress = null;       // HTTP服务器地址   
  7. private Thread emptier;                 // 回收站清理线程   
  8. private boolean stopRequested = false;          // 是否阻止请求   
  9. private boolean serviceAuthEnabled = false;         // 是否启动服务认证级别   
  10.   
  11. 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提供了一个执行初始化的方法,如下所示:

  1. /** 
  2.  * 初始化Namenode 
  3.  */  
  4. private void initialize(Configuration conf) throws IOException {  
  5.   InetSocketAddress socAddr = NameNode.getAddress(conf); // 从配置conf中获取到Namenode服务器所使用的Socket地址   
  6.   int handlerCount = conf.getInt("dfs.namenode.handler.count"10); // 服务器上处理器Handler线程的数量   
  7.   
  8.   if (serviceAuthEnabled = conf.getBoolean(erviceAuthorizationManager.SERVICE_AUTHORIZATION_CONFIG, false)) {  
  9.     PolicyProvider policyProvider =   
  10.       (PolicyProvider)(ReflectionUtils.newInstance(conf.getClass(PolicyProvider.POLICY_PROVIDER_CONFIG, HDFSPolicyProvider.class, PolicyProvider.class), conf));  
  11.     SecurityUtil.setPolicy(new ConfiguredPolicy(conf, policyProvider)); // 设置安全策略   
  12.   }  
  13.   
  14.   this.server = RPC.getServer(this, socAddr.getHostName(), socAddr.getPort(), handlerCount, false, conf); // 创建RPC服务器   
  15.   
  16.   this.serverAddress = this.server.getListenerAddress();   
  17.   FileSystem.setDefaultUri(conf, getUri(serverAddress));  
  18.   LOG.info("Namenode up at: " + this.serverAddress);  
  19.   
  20.   myMetrics = new NameNodeMetrics(conf, this); // 初始化NameNodeMetrics   
  21.   
  22.   this.namesystem = new FSNamesystem(this, conf);  
  23.   startHttpServer(conf);  
  24.   this.server.start();  // 启动RPC服务器   
  25.   startTrashEmptier(conf);  
  26. }  

在Namenode类实现中,其中实现的操作基本上都是由FSNamesystem来完成的。在这里,我们先不关心Namenode具体是如何实现那些基本操作的,而只是关注Namenode的功能特性,在后面再对FSNamesystem类进行分析。这里使用方式就是,根据Namenode所实现的接口中定义的操作,来分析Namenode服务器所具备的基本功能,或者说提供的基本服务。

NamenodeProtocol实现

在Namenode类中,实现了NamenodeProtocol协议接口中定义的getBlocks方法,如下所示:

  1. /** 
  2.  * 返回指定大小为size的Datanode上块及其所在位置列表 
  3.  */  
  4. public BlocksWithLocations getBlocks(DatanodeInfo datanode, long size) throws IOException {  
  5.   if(size <= 0) {  
  6.     throw new IllegalArgumentException("Unexpected not positive size: "+size);  
  7.   }  
  8.   return namesystem.getBlocks(datanode, size);  // 调用namesystem的getBlocks方法来真正实现   
  9. }  

给定DatanodeInfo datanode,它是一个描述Datanode的状态的实体类对象,通过getBlocks方法,可以获取到总的块大小为size的BlocksWithLocations,即描述这些块的位置信息的BlocksWithLocations对象。

ClientProtocol实现

实现ClientProtocol协议接口中定义的方法,如下所示:

 

  1. /** 
  2.  * 获取到指定文件src的全部块的信息 
  3.  *  
  4.  * @param src 指定的文件 
  5.  * @param offset 起始偏移位置 
  6.  * @param length 长度 
  7.  */  
  8. public LocatedBlocks   getBlockLocations(String src, long offset, long length) throws IOException {  
  9.   myMetrics.numGetBlockLocations.inc();  
  10.   return namesystem.getBlockLocations(getClientMachine(), src, offset, length); // 调用:获取到指定文件的全部块信息   
  11. }  
  12.   
  13. /** 
  14.  * 在制定的文件系统命名空间中创建一个文件入口(entry) 
  15.  */  
  16. public void create(String src, FsPermission masked, String clientName, boolean overwrite, short replication, long blockSize) throws IOException {  
  17.   String clientMachine = getClientMachine();  
  18.   if (stateChangeLog.isDebugEnabled()) {  
  19.     stateChangeLog.debug("*DIR* NameNode.create: file " +src+" for "+clientName+" at "+clientMachine);  
  20.   }  
  21.   if (!checkPathLength(src)) {  
  22.     throw new IOException("create: Pathname too long.  Limit " + MAX_PATH_LENGTH + " characters, " + MAX_PATH_DEPTH + " levels.");  
  23.   }  
  24.   namesystem.startFile(src,  
  25.       new PermissionStatus(UserGroupInformation.getCurrentUGI().getUserName(),  
  26.           null, masked),  
  27.       clientName, clientMachine, overwrite, replication, blockSize); // 调用:执行文件的创建操作   
  28.   myMetrics.numFilesCreated.inc();  
  29.   myMetrics.numCreateFileOps.inc();  
  30. }  
  31.   
  32. /** 
  33.  * 对指定文件执行追加写操作 
  34.  */  
  35. public LocatedBlock append(String src, String clientName) throws IOException {  
  36.   String clientMachine = getClientMachine();  
  37.   if (stateChangeLog.isDebugEnabled()) {  
  38.     stateChangeLog.debug("*DIR* NameNode.append: file " +src+" for "+clientName+" at "+clientMachine);  
  39.   }  
  40.   LocatedBlock info = namesystem.appendFile(src, clientName, clientMachine); // 调用:执行文件的追加写操作   
  41.   myMetrics.numFilesAppended.inc();  
  42.   return info;  
  43. }  
  44.   
  45. /** 
  46.  * 设置副本因子 
  47.  */  
  48. public boolean setReplication(String src, short replication) throws IOException {  
  49.   return namesystem.setReplication(src, replication); // 调用:设置副本因子   
  50. }  
  51.   
  52. /** 
  53.  * 设置权限 
  54.  */  
  55. public void setPermission(String src, FsPermission permissions) throws IOException {  
  56.   namesystem.setPermission(src, permissions); // 调用:设置权限   
  57. }  
  58.   
  59. /** 
  60.  * 设置文件属主 
  61.  */  
  62. public void setOwner(String src, String username, String groupname) throws IOException {  
  63.   namesystem.setOwner(src, username, groupname); // 调用:设置文件属主   
  64. }  
  65.   
  66. /** 
  67.  * 向指定的文件中写入数据块 
  68.  */  
  69. public LocatedBlock addBlock(String src, String clientName) throws IOException {  
  70.   stateChangeLog.debug("*BLOCK* NameNode.addBlock: file " +src+" for "+clientName);  
  71.   LocatedBlock locatedBlock = namesystem.getAdditionalBlock(src, clientName); // 调用:获取并执行写入操作   
  72.   if (locatedBlock != null)  
  73.     myMetrics.numAddBlockOps.inc();  
  74.   return locatedBlock;  
  75. }  
  76.   
  77. /** 
  78.  * 客户端放弃对指定块的操作 
  79.  */  
  80. public void abandonBlock(Block b, String src, String holder) throws IOException {  
  81.   stateChangeLog.debug("*BLOCK* NameNode.abandonBlock: " +b+" of file "+src);  
  82.   if (!namesystem.abandonBlock(b, src, holder)) { // 调用   
  83.     throw new IOException("Cannot abandon block during write to " + src);  
  84.   }  
  85. }  
  86.   
  87. /** 
  88.  * 客户端完成对指定文件的写操作 
  89.  */  
  90. public boolean complete(String src, String clientName) throws IOException {  
  91.   stateChangeLog.debug("*DIR* NameNode.complete: " + src + " for " + clientName);  
  92.   CompleteFileStatus returnCode = namesystem.completeFile(src, clientName); // 调用:执行写操作,写完成之后关闭该文件流   
  93.   if (returnCode == CompleteFileStatus.STILL_WAITING) {  
  94.     return false;  
  95.   } else if (returnCode == CompleteFileStatus.COMPLETE_SUCCESS) {  
  96.     return true;  
  97.   } else {  
  98.     throw new IOException("Could not complete write to file " + src + " by " + clientName);  
  99.   }  
  100. }  
  101.   
  102. /** 
  103.  * 客户端向Namenode报告corrupted块的信息  
  104.  */  
  105. public void reportBadBlocks(LocatedBlock[] blocks) throws IOException {  
  106.   stateChangeLog.info("*DIR* NameNode.reportBadBlocks");  
  107.   for (int i = 0; i < blocks.length; i++) {  
  108.     Block blk = blocks[i].getBlock();  
  109.     DatanodeInfo[] nodes = blocks[i].getLocations();  
  110.     for (int j = 0; j < nodes.length; j++) {  
  111.       DatanodeInfo dn = nodes[j];  
  112.       namesystem.markBlockAsCorrupt(blk, dn); // 调用:标记属于该Datanode的corrupted状态的块   
  113.     }  
  114.   }  
  115. }  
  116.   
  117. /** 
  118.  * 重命名文件 
  119.  */  
  120. public boolean rename(String src, String dst) throws IOException {  
  121.   stateChangeLog.debug("*DIR* NameNode.rename: " + src + " to " + dst);  
  122.   if (!checkPathLength(dst)) {  
  123.     throw new IOException("rename: Pathname too long.  Limit " + MAX_PATH_LENGTH + " characters, " + MAX_PATH_DEPTH + " levels.");  
  124.   }  
  125.   boolean ret = namesystem.renameTo(src, dst); // 调用:重命名文件   
  126.   if (ret) {  
  127.     myMetrics.numFilesRenamed.inc();  
  128.   }  
  129.   return ret;  
  130. }  
  131.   
  132. /** 
  133.  * 删除文件 
  134.  */  
  135. public boolean delete(String src, boolean recursive) throws IOException {  
  136.   if (stateChangeLog.isDebugEnabled()) {  
  137.     stateChangeLog.debug("*DIR* Namenode.delete: src=" + src + ", recursive=" + recursive);  
  138.   }  
  139.   boolean ret = namesystem.delete(src, recursive); // 调用:删除文件   
  140.   if (ret)   
  141.     myMetrics.numDeleteFileOps.inc();  
  142.   return ret;  
  143. }  
  144.   
  145. /** 
  146.  * 管理客户端状态,如果Namenode在一段时间内接收不到客户端心跳状态,标记为挂掉;经过周期检测,又获取到对应客户端心跳状态,为其解锁。 
  147.  */  
  148. public void renewLease(String clientName) throws IOException {  
  149.   namesystem.renewLease(clientName); // 调用    
  150. }  
  151.   
  152. /** 
  153.  * 获取指定目录下文件列表 
  154.  */  
  155. public FileStatus[] getListing(String src) throws IOException {  
  156.   FileStatus[] files = namesystem.getListing(src); // 调用   
  157.   if (files != null) {  
  158.     myMetrics.numGetListingOps.inc();  
  159.   }  
  160.   return files;  
  161. }  
  162.   
  163. /** 
  164.  * 获取指定文件的状态信息 
  165.  */  
  166. public FileStatus getFileInfo(String src)  throws IOException {  
  167.   return namesystem.getFileInfo(src); // 调用   
  168. }  
  169.   
  170. /** 
  171.  * 获取文件系统的统计信息 
  172.  */  
  173. public long[] getStats() throws IOException {  
  174.   return namesystem.getStats(); // 调用   
  175. }  
  176.   
  177. /** 
  178.  * 创建目录 
  179.  */  
  180. public boolean mkdirs(String src, FsPermission masked) throws IOException {  
  181.   stateChangeLog.debug("*DIR* NameNode.mkdirs: " + src);  
  182.   if (!checkPathLength(src)) {  
  183.     throw new IOException("mkdirs: Pathname too long.  Limit " + MAX_PATH_LENGTH + " characters, " + MAX_PATH_DEPTH + " levels.");  
  184.   }  
  185.   return namesystem.mkdirs(src,  
  186.       new PermissionStatus(UserGroupInformation.getCurrentUGI().getUserName(),  
  187.           null, masked)); // 调用   
  188. }  
  189.   
  190. /** 
  191.  * 设置安全模式 
  192.  */  
  193. public boolean setSafeMode(SafeModeAction action) throws IOException {  
  194.   return namesystem.setSafeMode(action); // 调用   
  195. }  
  196.   
  197. /** 
  198.  * 保存命名空间映像 
  199.  */  
  200. public void saveNamespace() throws IOException {  
  201.   namesystem.saveNamespace(); // 调用   
  202. }  
  203.   
  204. /** 
  205.  * 刷新Datanode结点的列表   
  206.  */  
  207. public void refreshNodes() throws IOException {  
  208.   namesystem.refreshNodes(new Configuration()); // 调用   
  209. }  
  210.   
  211. /** 
  212.  * 完成升级操作(删除在升级期间保存的文件系统的状态,一旦执行该方法,改变将不可逆转) 
  213.  */  
  214. public void finalizeUpgrade() throws IOException {  
  215.   namesystem.finalizeUpgrade(); // 调用   
  216. }  
  217.   
  218. /** 
  219.  * 报告升级进度 
  220.  */  
  221. public UpgradeStatusReport distributedUpgradeProgress(UpgradeAction action) throws IOException {  
  222.   return namesystem.distributedUpgradeProgress(action); // 调用   
  223. }  
  224.   
  225. /** 
  226.  * 保存Namenode的状态信息,写入到指定的文件中 
  227.  */  
  228. public void metaSave(String filename) throws IOException {  
  229.   namesystem.metaSave(filename); // 调用   
  230. }  
  231.   
  232. /** 
  233.  * 设置指定文件或目录的内容摘要信息 
  234.  */  
  235. public ContentSummary getContentSummary(String path) throws IOException {  
  236.   return namesystem.getContentSummary(path); // 调用   
  237. }  
  238.   
  239. /** 
  240.  * 设置指定目录的配额 
  241.  */  
  242. public void setQuota(String path, long namespaceQuota, long diskspaceQuota) throws IOException {  
  243.   namesystem.setQuota(path, namespaceQuota, diskspaceQuota); // 调用   
  244. }  
  245.   
  246. /** 
  247.  * 将指定文件的元数据写入到持久存储中 
  248.  */  
  249. public void fsync(String src, String clientName) throws IOException {  
  250.   namesystem.fsync(src, clientName); // 调用   
  251. }  
  252.   
  253. /** 
  254.  * 设置文件修改时间 
  255.  */  
  256. public void setTimes(String src, long mtime, long atime) throws IOException {  
  257.   namesystem.setTimes(src, mtime, atime); // 调用:设置文件修改时间   
  258. }  

DatanodeProtocol实现

Namenode需要与Datanode进行通信,所以必须实现与DatanodeProtocol协议接口。

下面是对DatanodeProtocol协议接口中定义的基本操作的实现:

  1. /** 
  2.  * Datanode向Namenode注册,返回Namenode需要验证的有关Datanode的信息DatanodeRegistration的实例  
  3.  */  
  4. public DatanodeRegistration register(DatanodeRegistration nodeReg) throws IOException {  
  5.   verifyVersion(nodeReg.getVersion()); // 验证协议版本   
  6.   namesystem.registerDatanode(nodeReg); // 调用:注册指定的Datanode结点         
  7.   return nodeReg;  
  8. }  
  9.   
  10. /** 
  11.  * Datanode向Namenode发送心跳状态报告,显示其当前状态 
  12.  */  
  13. public DatanodeCommand[] sendHeartbeat(DatanodeRegistration nodeReg,  
  14.                                      long capacity,  
  15.                                      long dfsUsed,  
  16.                                      long remaining,  
  17.                                      int xmitsInProgress,  
  18.                                      int xceiverCount) throws IOException {  
  19.   verifyRequest(nodeReg);  
  20.   return namesystem.handleHeartbeat(nodeReg, capacity, dfsUsed, remaining, xceiverCount, xmitsInProgress); // 调用:处理Datanode发送的心跳信息   
  21. }  
  22.   
  23. /** 
  24.  * 指定Datanode向Namenode报告其上的块状态报告  
  25.  */  
  26. public DatanodeCommand blockReport(DatanodeRegistration nodeReg, long[] blocks) throws IOException {  
  27.   verifyRequest(nodeReg); // 验证Datanode向Namenode请求的合法性   
  28.   BlockListAsLongs blist = new BlockListAsLongs(blocks);  
  29.   stateChangeLog.debug("*BLOCK* NameNode.blockReport: " +"from "+nodeReg.getName()+" "+blist.getNumberOfBlocks() +" blocks");  
  30.   namesystem.processReport(nodeReg, blist); // 调用:处理块状态报告   
  31.   if (getFSImage().isUpgradeFinalized())  
  32.     return DatanodeCommand.FINALIZE;  
  33.   return null;  
  34. }  
  35.   
  36. /** 
  37.  * Datanode向Namenode报告最近接收到的数据块,以及对数据块的操作信息 
  38.  */  
  39. public void blockReceived(DatanodeRegistration nodeReg,  Block blocks[], String delHints[]) throws IOException {  
  40.   verifyRequest(nodeReg); // 验证请求是否合法   
  41.   stateChangeLog.debug("*BLOCK* NameNode.blockReceived: " +"from "+nodeReg.getName()+" "+blocks.length+" blocks.");  
  42.   for (int i = 0; i < blocks.length; i++) {  
  43.     namesystem.blockReceived(nodeReg, blocks[i], delHints[i]); // 调用   
  44.   }  
  45. }  
  46.   
  47. /** 
  48.  * 报告错误信息 
  49.  */  
  50. public void errorReport(DatanodeRegistration nodeReg, int errorCode, String msg) throws IOException {  
  51.   String dnName = (nodeReg == null ? "unknown DataNode" : nodeReg.getName());  
  52.   LOG.info("Error report from " + dnName + ": " + msg); // 将来自指定的Datanode的出错信息登录到日志文件中   
  53.   if (errorCode == DatanodeProtocol.NOTIFY) {  
  54.     return;  
  55.   }  
  56.   verifyRequest(nodeReg);  
  57.   if (errorCode == DatanodeProtocol.DISK_ERROR) { // 如果是对应的Datanode出现了磁盘错误   
  58.     namesystem.removeDatanode(nodeReg); // 调用:删除该Datanode的描述信息   
  59.   }  
  60. }  
  61.   
  62. /**  
  63.  * 在分布式系统升级过程中,向Namenode发送命令 
  64.  */  
  65. public UpgradeCommand processUpgradeCommand(UpgradeCommand comm) throws IOException {  
  66.   return namesystem.processDistributedUpgradeCommand(comm); // 调用   
  67. }  

其它实现

用于Namenode与客户端通信,还定义了如下几个与Namenode管理文件系统命名空间相关的操作,主要是对EditLog和FsImage文件的操作:

  1. /** 
  2.  * 当前EditLog日志文件的大小 
  3.  */  
  4. public long getEditLogSize() throws IOException {  
  5.   return namesystem.getEditLogSize(); // 调用   
  6. }  
  7.   
  8. /** 
  9.  * 回滚EditLog文件 
  10.  */  
  11. public CheckpointSignature rollEditLog() throws IOException {  
  12.   return namesystem.rollEditLog(); // 调用   
  13. }  
  14.   
  15. /** 
  16.  * 回滚FsImage映像文件  
  17.  */  
  18. public void rollFsImage() throws IOException {  
  19.   namesystem.rollFSImage(); // 调用   
  20. }  
  21.   
  22. /** 
  23.  * 获取FsImage映像文件名称 
  24.  */  
  25. public File getFsImageName() throws IOException {  
  26.   return getFSImage().getFsImageName();  
  27. }  
  28.     
  29. /** 
  30.  * 获取FsImage映像文件 
  31.  */  
  32. public FSImage getFSImage() {  
  33.   return namesystem.dir.fsImage;  
  34. }  
  35.   
  36. /** 
  37.  * 获取到FsImage映像文件的所有检查点 
  38.  */  
  39. public File[] getFsImageNameCheckpoint() throws IOException {  
  40.   return getFSImage().getFsImageNameCheckpoint();  
  41. }  

在Namenode上,对文件系统名字空间进行的任何操作,Hadoop采用记录事务日志的方式来保存这些操作的记录,对应的事务日志文件就是EditLog文件,该文件存放在磁盘上。而FsImage映像文件,是Hadoop集群工作的过程中使用的,便于Namenode进程管理文件系统的命名空间。当Namenode启动的时候,会首先读取EditLog与FsImage文件,并将EditLog作用于FsImage,因为此时FsImage文件一定是最新的事务日志记录文件的映像,并将EditLog文件删除(因为它可能会在Hadoop集群工作过程中变成旧的文件,也就是不是最新的事务记录);在Namenode启动之后工作的过程中,仍然会有对文件系统命名空间执行操作的事务记录,这些记录都被保存到一个新的EditLog事务日志文件中,以便下次启动Namenode的时候,将最新的事务日志记录作用于FsImage上。

另外,Namenode类中实现了创建一个Namenode实例的方法createNameNode,该方法是static的,通过命令行参数一个Hadoop配置类实例来创建一个Namenode实例,方法实现如下所示:

  1. public static NameNode createNameNode(String argv[], Configuration conf) throws IOException {  
  2.   if (conf == null)  
  3.     conf = new Configuration(); // 如果指定的Configuration类实例为null,创一个默认的配置类实例   
  4.   StartupOption startOpt = parseArguments(argv); // 解析命令行获取到Namenode启动参数   
  5.   if (startOpt == null) {  
  6.     printUsage(); // 如果解析命令行参数返回null,打印命令用法信息   
  7.     return null;  
  8.   }  
  9.   setStartupOption(conf, startOpt); // 设置Namenode启动选项   
  10.   
  11.   switch (startOpt) { // 根据设置的参数选项,进行处理   
  12.     case FORMAT:  
  13.       boolean aborted = format(conf, true);  
  14.       System.exit(aborted ? 1 : 0);  
  15.     case FINALIZE:  
  16.       aborted = finalize(conf, true);  
  17.       System.exit(aborted ? 1 : 0);  
  18.     default:  
  19.   }  
  20.   
  21.   NameNode namenode = new NameNode(conf); // 构造一个Namenode实例   
  22.   return namenode;  
  23. }  

关于配置启动选项,可以查看org.apache.hadoop.hdfs.server.common.HdfsConstants接口中定义了枚举类StartupOption,如下所示:

  1. static public enum StartupOption{  
  2.   FORMAT  ("-format"), // 格式化HDFS选项   
  3.   REGULAR ("-regular"), // 正常启动选项   
  4.   UPGRADE ("-upgrade"), // 分布式升级选项   
  5.   ROLLBACK("-rollback"), // 事务回滚选项   
  6.   FINALIZE("-finalize"), // 确认升级完成选项   
  7.   IMPORT  ("-importCheckpoint"); //导入检查点选项   
  8.     
  9.   private String name = null;  
  10.   private StartupOption(String arg) {this.name = arg;}  
  11.   public String getName() {return name;}  
  12. }  

上面创建一个Namenode,在解析完命令行参数后,得到一个枚举类实例,然后根据它来设置Namenode启动选项,如下所示:

  1. private static void setStartupOption(Configuration conf, StartupOption opt) {  
  2.   conf.set("dfs.namenode.startup", opt.toString()); // 实际上,将命令行解析得到的启动选项,设置到一个Confirmation配置实例上   
  3. }  

接着,在构造Namenode实例之前,根据设置的附加启动选项,做一个预处理(格式化或某次升级之后的确认动作),如果是格式化操作,具体操作为:

  1. /** 
  2.  * 验证配置的目录是否存在,如果验证合法则执行格式化操作 
  3.  */  
  4. private static boolean format(Configuration conf, boolean isConfirmationNeeded) throws IOException {  
  5.   Collection<File> dirsToFormat = FSNamesystem.getNamespaceDirs(conf); // 通过配置实例conf获取到需要进行格式化的文件系统命名空间目录   
  6.   Collection<File> editDirsToFormat = FSNamesystem.getNamespaceEditsDirs(conf); // 获取到需要进行格式化的文件系统命名空间修改目录    
  7.   for(Iterator<File> it = dirsToFormat.iterator(); it.hasNext();) { // 验证上述目录的合法性   
  8.     File curDir = it.next();  
  9.     if (!curDir.exists())  
  10.       continue;  
  11.     if (isConfirmationNeeded) {  
  12.       System.err.print("Re-format filesystem in " + curDir +" ? (Y or N) ");  
  13.       if (!(System.in.read() == 'Y')) {  
  14.         System.err.println("Format aborted in "+ curDir);  
  15.         return true// 放弃对文件系统命名空间的格式化操作   
  16.       }  
  17.       while(System.in.read() != '/n'); // discard the enter-key   
  18.     }  
  19.   }  
  20.   
  21.   FSNamesystem nsys = new FSNamesystem(new FSImage(dirsToFormat, editDirsToFormat), conf); // 基于构造的FsImage文件实例来创建文件系统命名空间   
  22.   nsys.dir.fsImage.format(); // 对FsImage文件进行格式化   
  23.   return false;  
  24. }  

关于FsImage与EditLog在文件系统中的所在位置的选择,在org.apache.hadoop.hdfs.server.namenode.FSNamesystem类中可以看到详细的情况。

如果是确认升级完成命令行,则实现如下所示:

  1. private static boolean finalize(Configuration conf, boolean isConfirmationNeeded) throws IOException {  
  2.   Collection<File> dirsToFormat = FSNamesystem.getNamespaceDirs(conf); // 文件系统命名空间文件FsImage所在目录   
  3.   Collection<File> editDirsToFormat = FSNamesystem.getNamespaceEditsDirs(conf); // EditLog所在目录   
  4.   FSNamesystem nsys = new FSNamesystem(new FSImage(dirsToFormat, editDirsToFormat), conf); // 构造FSNamesystem   
  5.   System.err.print(  
  6.       "/"finalize/" will remove the previous state of the files system./n"  
  7.       + "Recent upgrade will become permanent./n"  
  8.       + "Rollback option will not be available anymore./n");  
  9.   if (isConfirmationNeeded) {  
  10.     System.err.print("Finalize filesystem state ? (Y or N) ");  
  11.     if (!(System.in.read() == 'Y')) {  
  12.       System.err.println("Finalize aborted.");  
  13.       return true// 放弃升级确认   
  14.     }  
  15.     while(System.in.read() != '/n'); // discard the enter-key   
  16.   }  
  17.   nsys.dir.fsImage.finalizeUpgrade(); // 调用FSImage类的finalizeUpgrade方法实现升级完成的确认   
  18.   return false;  
  19. }  

关于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服务器进程。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值