安全模式是HDFS所处的一种特殊状态,在这种状态下,文件系统只接受读数据请求,而不接受删除、修改等变更请求。在NameNode主节点启动时,HDFS首先进入安全模式,DataNode在启动的时候会向namenode汇报可用的block等状态,当整个系统达到安全标准时,HDFS自动离开安全模式。如果HDFS出于安全模式下,则文件block不能进行任何的副本复制操作,因此达到最小的副本数量要求是基于datanode启动时的状态来判定的,启动时不会再做任何复制(从而达到最小副本数量要求)
HDFS 安全模式的理解
安全模式是hadoop的一种保护机制,用于保证集群中的数据块的安全性。
当集群启动的时候,会首先进入安全模式。当系统处于安全模式时会检查数据块的完整性。假设我们设置的副本数(即参数)是5,那么在datanode上就应该有5个副本存在,假设只存在3个副本,那么比例就是3/5=0.6。在配置文件中定义了一个最小的副本的副本率0.999,如图
我们的副本率0.6明显小于0.99,因此系统会自动的复制副本到其他的dataNode,使得副本率不小于0.999.如果系统中有8个副本,超过我们设定的5个副本,那么系统也会删除多余的3个副本。
安全模式对我们有什么影响呢?
这时,不允许客户端进行任何修改文件的操作,包括上传文件,删除文件,重命名,创建文件夹等操作。比如创建文件时,在源代码中就有对安全模式的判断,如图所示
当我们在安全模式下进行修改文件操作时,会报如下错误,如图
正常情况下,安全模式会运行一段时间自动退出的,只需要我们稍等一会就行了,到底等多长时间呢,我们可以通过50070端口查看安全模式退出的剩余时间,如图
虽然不能进行修改文件的操作,但是可以浏览目录结构、查看文件内容的。
在命令行下是可以控制安全模式的进入、退出和查看的。
命令 hadoop fs -safemode get 查看安全模式状态
命令 hadoop fs -safemode enter 进入安全模式状态
命令 hadoop fs -safemode leave 离开安全模式
操作如图所示
安全模式是hadoop的一种保护机制,在启动时,最好是等待集群自动退出,然后进行文件操作。
下面是namenode的一个日志片段:
这里写图片描述
安全模式相关的配置
系 统什么时候才离开安全模式,需要满足哪些条件?
当收到来自datanode的状态报告后,namenode根据配置,确定
1)可用的block占总数的比例、
2)可用的数据节点数量符合要求之后,离开安全模式。如果有必要,也可以通过命令强制离开安全模式。与安全模式相关的主要配置在文件中,主要有下面几个属性
: 最小的文件block副本数量,默认为1.
-pct: 副本数达到最小要求的block占系统总block数的百分比,当实际比例超过该配置后,才能离开安全模式(但是还需要其他条件也满足)。默认为0.999f,也就是说符合最小副本数要求的block占比超过99.9%时,并且其他条件也满足才能离开安全模式。如果为小于等于0,则不会等待任何副本达到要求即可离开。如果大于1,则永远处于安全模式。
: 离开安全模式的最小可用(alive)datanode数量要求,默认为0.也就是即使所有datanode都不可用,仍然可以离开安全模式。
: 当集群可用block比例,可用datanode都达到要求之后,如果在extension配置的时间段之后依然能满足要求,此时集群才离开安全模式。单位为毫秒,默认为1.也就是当满足条件并且能够维持1毫秒之后,离开安全模式。 这个配置主要是对集群的稳定程度做进一步的确认。避免达到要求后马上又不符合安全标准。
总结一下,要离开安全模式,需要满足以下条件:
1)达到副本数量要求的block比例满足要求;
2)可用的datanode节点数满足配置的数量要求;
3) 1、2 两个条件满足后维持的时间达到配置的要求。
相关的操作命令
Hadoop提供脚本用于对安全模式进行操作,主要命令为:
hadoop dfsadmin -safemode <command>
command的可用取值如下:
command 功能 备注
get 查看当前状态
enter 进入安全模式
leave 强制离开安全模式
wait 一直等待直到安全模式结束
相关源码
安全模式的相关操作封装在SafeMode接口中:
/** 安全模式相关操作 */
@
public interface SafeMode {
/**
* 检查进入或者退出安全模式的条件是否满足,
* 如果满足,进入或退出安全模式。
*/
public void checkSafeMode();
/** 系统当前是否处于安全模式? */
public boolean isInSafeMode();
/**
* 系统启动时是否自动进入安全模式?
*/
public boolean isInStartupSafeMode();
/** Check whether replication queues are being populated. */
public boolean isPopulatingReplQueues();
/**
* 增加达到最小副本数要求的block数
* @param replication current replication
*/
public void incrementSafeBlockCount(int replication);
/** 降低达到最小副本数要求的block数 */
public void decrementSafeBlockCount(Block b);
}
HDFS的命名系统需要提供安全模式相关的操作,因此实现了改接口,层次结构如下:
这里写图片描述
Namesystem继承了SafeMode接口,FSNamesystem类实现了Namesystem,间接实现SafeMode定义的操作,也就是我们可以对命名系统进行安全模式相关的操作。
FSNamesystem内部实现了一个内部线程类SafeModeMonitor,周期性地检查是否应该离开安全模式:
/**
* 周期性地检测是否可以离开安全模式,逻辑封装在run方法中.
* This thread starts when the threshold level is reached.
*
*/
class SafeModeMonitor implements Runnable {
/** 两次检测间隔的毫秒数,即1秒 */
private static final long recheckInterval = 1000;
/**
*/
@Override
public void run() {
// 系统运行时,循环检测
while (fsRunning) {
writeLock();
try {
// 没有安全模式相关信息,也就是不在安全模式
if (safeMode == null) { // Not in safe mode.
break;//线程退出
}
if (()) {
// Leave safe mode.
();
smmthread = null;
// 离开安全模式后,线程退出
break;
}
} finally {
writeUnlock();
}
try {
// 两次检测之间,线程休眠
(recheckInterval);
} catch (InterruptedException ie) {
// Ignored
}
}
// 当系统不在运行的时候,线程结束退出
if (!fsRunning) {
("NameNode is being shutdown, exit SafeModeMonitor thread");
}
}
}
这个内部类中出现的safeMode,其类型为SafeModeInfo,用于保存安全模式下的相关信息:
private volatile SafeModeInfo safeMode; // safe mode information
注意到这个变量为volatile,也就是说线程对该变量的任何修改完成后,其他线程立刻可以看到变化。OK,那么什么时候会创建这个对象呢,我们用safeMode= new SafeModeInfo搜索源代码,发现2个地方会实例化这个对象:一个实在FSNamesystem的构造函数中,另一处在FSNamesystem类的enterSafeMode方法中。我们先来看构造函数,其方法声明如下:
FSNamesystem(Configuration conf, FSImage fsImage, boolean ignoreRetryCache)
throws IOException
这个构造函数使用给定的配置及文件镜像,创建一个文件命名系统。其中有一句如下:
= new SafeModeInfo(conf);
我们去看看SafeModeInfo这个构造函数:
/**
* Creates SafeModeInfo when the name node enters
* automatic safe mode at startup.
*
* @param conf configuration
*/
private SafeModeInfo(Configuration conf) {
// 这个配置就是我们前面提到的百分比配置
= (DFS_NAMENODE_SAFEMODE_THRESHOLD_PCT_KEY,
DFS_NAMENODE_SAFEMODE_THRESHOLD_PCT_DEFAULT);
if(threshold > 1.0) {
("The threshold value should't be greater than 1, threshold: " + threshold);
}
// 这个也是我们前面提到的最小可用datanode数量配置
= (
DFS_NAMENODE_SAFEMODE_MIN_DATANODES_KEY,
DFS_NAMENODE_SAFEMODE_MIN_DATANODES_DEFAULT);
= (DFS_NAMENODE_SAFEMODE_EXTENSION_KEY, 0);
= (DFS_NAMENODE_REPLICATION_MIN_KEY,
DFS_NAMENODE_REPLICATION_MIN_DEFAULT);
(DFS_NAMENODE_SAFEMODE_THRESHOLD_PCT_KEY + " = " + threshold);
(DFS_NAMENODE_SAFEMODE_MIN_DATANODES_KEY + " = " + datanodeThreshold);
(DFS_NAMENODE_SAFEMODE_EXTENSION_KEY + " = " + extension);
// default to safe mode threshold (., don't populate queues before leaving safe mode)
=
(DFS_NAMENODE_REPL_QUEUE_THRESHOLD_PCT_KEY,
(float) threshold);
= 0;
= 0;
}
核心逻辑就是获取相关的配置,创建初始的SafeModeInfo。
看看另一个地方:
/**
* Enter safe mode. If resourcesLow is false, then we assume it is manual
* 参数用于区分是因为资源不满足要求还是手动进入安全模式
* @throws IOException
*/
void enterSafeMode(boolean resourcesLow) throws IOException {
writeLock();
try {
// Stop the secret manager, since rolling the master key would
// try to write to the edit log
stopSecretManager();
// Ensure that any concurrent operations have been fully synced
// before entering safe mode. This ensures that the FSImage
// is entirely stable on disk as soon as we're in safe mode.
boolean isEditlogOpenForWrite = getEditLog().isOpenForWrite();
// Before Editlog is in OpenForWrite mode, editLogStream will be null. So,
// logSyncAll call can be called only when Edlitlog is in OpenForWrite mode
if (isEditlogOpenForWrite) {
getEditLog().logSyncAll();
}
if (!isInSafeMode()) {
safeMode = new SafeModeInfo(resourcesLow);
return;
}
if (resourcesLow) {
();
} else {
();
}
if (isEditlogOpenForWrite) {
getEditLog().logSyncAll();
}
("STATE* Safe mode is ON"
+ ());
} finally {
writeUnlock();
}
}
总结一下,SafeModeMonitor作为守护线程,在收到来自datanode的BlockReport状态报告之后,周期性地检测是否达到离开安全模式的条件,如果符合条件,则离开安全模式。