用户对HDFS的每步操作都会先记录到NameNode磁盘中的EditLog文件中。为了避免频繁操作磁盘对用户操作效率的影响,HDFS为EditLog在内存中分配两个buffer:一个用于接收用户操作指令;另外一个在用户想要刷新第一个buffer内数据时开始担当接收用户指令的职责。之后两块buffer不断切换角色,进行着上述操作。
正是因为有内存中buffer的存在,当管理员shutdown NameNode时,就会丢失用户写入buffer且还没来得及刷新到磁盘中的那些操作记录。我们需要做的就是在NameNode加入shutdown hook,希望当NameNode接收到系统的关闭信号时,能正常刷新buffer中数据到磁盘中。
如果我们能改动NameNode的代码,那么就可以像这样加入:(HDFS0.21)
// NameNode.java public static void main(String argv[]) throws Exception { try { StringUtils.startupShutdownMessage(NameNode.class, argv, LOG); final NameNode namenode = createNameNode(argv, null); if (namenode != null) Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { namenode.namesystem.close(); LOG.info("Close FSNameSystem successfully"); } }); namenode.join(); } catch (Throwable e) { LOG.error(StringUtils.stringifyException(e)); System.exit(-1); } }
如果不能改动代码的话,可以通过HDFS提供的Service plugin来完成。Service plugin是HDFS提供给管理员的access point,我们可以通过这个access point为HDFS提供一些扩展功能。同样地,创建一个关闭EditLog的plugin。
创建一个EditLogPlugin的类,注意它的包名应该与NameNode在同一包下面,这是因为nameSystem只有在这个包下面才可以访问。
package org.apache.hadoop.hdfs.server.namenode; import java.io.IOException; import org.apache.hadoop.util.ServicePlugin; public class EditLogPlugin implements ServicePlugin { public void start(Object arg0) { final NameNode nameNode = (NameNode) arg0; Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { nameNode.namesystem.close(); NameNode.LOG.info("Close FSNameSystem successfully"); } }); } public void stop() { } public void close() throws IOException { } }
打包这个类,然后把它放到Hadoop的lib目录下。在HDFS的配置文件中这样配:
<property> <name>dfs.namenode.plugins</name> <value>org.apache.hadoop.hdfs.server.namenode.EditLogPlugin</value> </property>
这样在NameNode启动后shutdown hook就会加入到系统中。
但需要注意的是,NameNode中的shutdown hook只有在NameNode进程可以正常拦截系统进程的情况下才可以起作用。像SIGKILL(9)这样的系统信号是不能被进程接收、处理的。所以我们对EditLog正常关闭的期望是很局限的,只有尽最大可能地保证管理脚本和管理操作中不要有“kill -9 <pid>”这样的命令。