前言
什么是“错误注入”?错误注入指的是将错误引入到我们的程序中。可能有人会很好奇,这么做有什么目的呢?答案很简单:程序的测试。因为在很多时候,当我们要进行边缘情况测试的时候,往往模拟测试的场景不是非常好造的(尤其是分布式类的程序更是如此),这个时候,我们需要有快捷的方式将错误注入到程序中,以便在我们需要发生错误时,进行错误的产生。本文笔者将结合HDFS现有的错误注入方法来介绍此部分内容。
错误注入技术的原理
错误注入技术的一个核心关键词是“拦截”。正常情况下,应用程序是不会发生错误等异常的。而错误注入器的作用是对用户应用程序中的某个方法进行拦截,然后将错误注入到此处,然后接着往下执行,结果是程序终止了还是错误被处理了则还要看应用程序本身。
拦截的层面可以根据应用程序本身而定,比较简单的可以在应用程序同级别进行拦截,比较高级的是在OS层面,在系统回调方法处进行拦截。下图是原理图。
钩子拦截示意图
HDFS内部的几种错误注入方法
HDFS作为一个底层的存储系统,它的错误注入更多地偏向于是文件、数据块的错误注入。但是笔者认为错误注入的原理和技巧还是可以通用的。
结合应用程序规则的错误注入
首先笔者先来介绍一种比较简单的错误引入的方法。在HDFS内部,当DataNode内部的DiskChecker线程发现文件不能被执行,或突然丢失了,则会认为此文件所在的盘就为坏盘了。所以根据这个原则,我们可以得到下面2类错误注入的方法。
第一类,改变文件的属性,比如说变为只读。代码如下:
...
// fail the volume
// delete/make non-writable one of the directories (failed volume)
data_fail = new File(dataDir, "data3");
failedDir = MiniDFSCluster.getFinalizedDir(data_fail,
cluster.getNamesystem().getBlockPoolId());
if (failedDir.exists() &&
//!FileUtil.fullyDelete(failedDir)
!deteteBlocks(failedDir)
) {
throw new IOException("Could not delete hdfs directory '" + failedDir + "'");
}
// 设置文件目录为只读模式
data_fail.setReadOnly();
failedDir.setReadOnly();
System.out.println("Deleteing " + failedDir.getPath() + "; exist=" + failedDir.exists());
// access all the blocks on the "failed" DataNode,
// we need to make sure that the "failed" volume is being accessed -
// and that will cause failure, blocks removal, "emergency" block report
// 触发一次文件文件访问操作
triggerFailure(filename, filesize);
...
设置为只读方式,在随后的读块操作中,将会发现这个错误。
第二类,重命名原始文件,使系统认为原始文件发生丢失。重命名的好处是还可以再立刻恢复回去,而不是真的删除掉了。操作代码如下:
public static