bigdataprimary 2013-05-21
在分布式文件系统启动的时候,开始的时候会有安全模式,当分布式文件系统处于安全模式的情况下,文件系统中的内容不允许修改也不允许删除,直到安全模式结 束。安全模式主要是为了系统启动的时候检查各个DataNode上数据块的有效性,同时根据策略必要的复制或者删除部分数据块。运行期通过命令也可以进入 安全模式。在实践过程中,系统启动的时候去修改和删除文件也会有安全模式不允许修改的出错提示,只需要等待一会儿即可。 SafeModeException 异常,运行hadoop程序时,有时候会报以下错误:org.apache.hadoop.dfs.SafeModeException: Cannot delete/user/hadoop/input. Name node is in safe mode.只有离开安全模式,系统才会正常读写。
NameNode在启动的时候首先进入安全模式,如果datanode丢失的block达到一定的比例(1- dfs.safemode.threshold.pct),则系统会一直处于安全模式状态即只读状态。 dfs.safemode.threshold.pct(缺省值0.999f)表示HDFS启动的时候,如果DataNode上报的block个数达到了 元数据记录的block个数的0.999倍才可以离开安全模式,否则一直是这种只读模式。如果设为1则HDFS永远是处于SafeMode。
(1)修改dfs.safemode.threshold.pct为一个比较小的值,缺省是0.999。
(2)hadoop dfsadmin -safemode leave命令强制离开
hadoop dfsadmin-safemode 命令 格式:Usage: java DFSAdmin [-safemode enter | leave | get |wait] 用户可以通过dfsadmin -safemode value 来操作安全模式,参数value的说明如下: enter - 进入安全模式 leave - 强制NameNode离开安全模式 get - 返回安全模式是否开启的信息 wait - 等待,一直到安全模式结束。
FSNamesystem在初始化的时侯会首先进入安全模式状态并在之后开启一个后台工作线程——SafeModeMonitor来监控当前集群是否可以离开安全模式状态。 FSNamesystem是通过一个SafeModeInfo对象来保存当前集群的状态信息的。FSNamesystem在初始化的时侯会创建它对应的一个实例,并传入当前集群数据块的总数量。
class SafeModeInfo { // 配置Field,从与配置文件相关的配置类实例获取到这些属性值 /** 只有当条件(是个比率)大于threshold的时候,才能进入安全模式*/ private double threshold; /** 进入安全模式 */ private int extension; /** 安全模式要求的最小副本因子 */ private int safeReplication; /** * 当达到threshold的值的时间 * -1 离开安全模式 * 0 正在安全模式下,但是threshold是不可达的 */ private long reached = -1; /** 块总数 */ int blockTotal; /** 安全的块的总数 */ private int blockSafe; /** 输出最后状态的时间 */ private long lastStatusReport = 0; /** * Creates SafeModeInfo when the name node enters * automatic safe mode at startup. * * @param conf configuration */ SafeModeInfo(Configuration conf) { this.threshold = conf.getFloat("dfs.safemode.threshold.pct", 0.95f); this.extension = conf.getInt("dfs.safemode.extension", 0); this.safeReplication = conf.getInt("dfs.replication.min", 1); this.blockTotal = 0; this.blockSafe = 0; } /** * Creates SafeModeInfo when safe mode is entered manually. * * The {@link #threshold} is set to 1.5 so that it could never be reached. * {@link #blockTotal} is set to -1 to indicate that safe mode is manual. * * @see SafeModeInfo */ private SafeModeInfo() { this.threshold = 1.5f; // this threshold can never be reached this.extension = Integer.MAX_VALUE; this.safeReplication = Short.MAX_VALUE + 1; // more than maxReplication this.blockTotal = -1; this.blockSafe = -1; this.reached = -1; enter(); reportStatus("STATE* Safe mode is ON.", true); } ....... }
第一步:FSNamesystem的initialize 中会判断是否进入安全模式
//加载安全模式信息,进入安全模式 this.safeMode = new SafeModeInfo(conf); //将block的总数设置给safemode etBlockTotal();
当给SafeModeInfo对象设置当前集群中数据块总量的时候,它会判断当前集群是否应该进入安全模式状态,如果应该则它会让当前集群立即进入安全模 式。至于如何判断集群是否应该进入安全模式状态,主要是依据当前主节点收到数据节点报告的数据块Block处于安全状态的数量占集群总数据块的比重是否达 到系统设置的阈值threshold来确定的。
第二步:设置Block总数
synchronized void setBlockTotal(int total) { this.blockTotal = total; checkMode(); }
第三步:datanode块汇报时不断调用checkMode方法
synchronized void incrementSafeBlockCount(short replication) { if ((int)replication == safeReplication) this.blockSafe++; checkMode(); } synchronized void decrementSafeBlockCount(short replication) { if (replication == safeReplication-1) this.blockSafe--; checkMode(); } /** * Check and trigger safe mode if needed. * 检查当前文件系统是否应该进入安全模式状态 */ private void checkMode() { if (needEnter()) { enter(); reportStatus("STATE* Safe mode ON.", false); return; } // the threshold is reached // //当前文件系统是否可以离开安全模式状态 if (!isOn() || // safe mode is off extension <= 0 || threshold <= 0) { // don't need to wait this.leave(true); // leave safe mode return; } if (reached > 0) { // threshold has already been reached before reportStatus("STATE* Safe mode ON.", false); return; } LOG.info("start monitor"); // start monitor // 开启一个监控线程来判断当前文件系统是否可以真正离开 reached = now(); smmthread = new Daemon(new SafeModeMonitor()); smmthread.start(); reportStatus("STATE* Safe mode extension entered.", true); }
一个数据块Block处于安全状态指的是该数据块的副本数量(根据数据节点的数据块报告)达到了safeReplication。集群在进入安全模式之 后,主节点在处理数据节点关于数据块Block报告的过程中会不断的调用SafeModeInfo的checkMode()来判断当前集群中处于安全状态 的数据块占集群总数据块的比重是否达到系统设置的阈值threshold。
第四步:到达阀值,开启SafeModeMonitor线程如果块汇报到一定程度也就是安全状态的数据块占集群总数据块的比重是否达到系统设置的阈值threshold。系统会开启SafeModeMonitor 线程
smmthread = new Daemon(new SafeModeMonitor()); smmthread.start();
第五步:稳定期过度canLeave到了这个阈值,集群也需要一个过渡期extension来确保当前安全数据块的数量是否已经稳定了。所以它会立即开启一个SafeModeMonitor 后台线程来通过轮询的方式判断当前集群是否度过了这个稳定期,如果在稳定期之后,当前就能的安全数据块数量任然超过了设定的阈值则此时集群可以真正离开安 全模式了。这里extension的默认值为0,但也可以通过配置文件来设置,对应的配置项为dfs.safemode.extention。
/** * Safe mode can be turned off iff * the threshold is reached and * the extension time have passed. * 判断当前集群是否可以离开安全模式 reached:到达阀值时间 extension:缓存时间 * @return true if can leave or false otherwise. */ synchronized boolean canLeave() { if (reached == 0) return false; if (now() - reached < extension) { reportStatus("STATE* Safe mode ON.", false); return false; } return !needEnter(); }
第六步:离开安全模式
集群在离开安全模式的时候会对集群中的所有数据块Blocks进行处理。
/** * 处理当前文件系统的所有数据块: * 1).无效数据块 * 2).数据块副本不够 * 3).数据块副本有多余 */ private synchronized void processMisReplicatedBlocks() { long nrInvalid = 0, nrOverReplicated = 0, nrUnderReplicated = 0; neededReplications.clear(); for(BlocksMap.BlockInfo block : blocksMap.getBlocks()) { INodeFile fileINode = block.getINode(); //数据块已不属于任何文件,所以应该从所有存储该数据块副本的数据节点上清除它 if(fileINode == null) { // block does not belong to any file nrInvalid++; addToInvalidates(block); continue; } // calculate current replication short expectedReplication = fileINode.getReplication(); NumberReplicas num = countNodes(block); int numCurrentReplica = num.liveReplicas(); // add to under-replicated queue if need to be //判断当前数据块是否还需要复制副本,如果需要则将其添加到待复制副本队列中 if (neededReplications.add(block, numCurrentReplica, num.decommissionedReplicas(), expectedReplication)) { nrUnderReplicated++; } //处理数据块的多于副本 if (numCurrentReplica > expectedReplication) { // over-replicated block nrOverReplicated++; processOverReplicatedBlock(block, expectedReplication, null, null); } } }