hadoop启动会进过一个安全模式,安全模式有如下几个特点:
安全模式下namenode收集各个datanode的报告,当数据块达到最小副本数(复制因子)以上时,会被认为是“安全”的 。
在一定比例(可设置)的数据块被确定为“安全”后,再过若干时间,安全模式结束。
当检测到副本数不足的数据块时,该块会被复制直到达到最小副本数 。
程序使用Data类模拟数据块,Namenode类作为namenode节点,Datanode类作为datanode数据节点。程序如下:
Data类
public class Data {
private int id;
private String msg;
public Data(int id, String msg) {
this.id = id;
this.msg = msg;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
Datanode类
import java.util.ArrayList;
import java.util.List;
public class Datanode {
private Namenode namenode;
private String name;
private List<Data> datas = new ArrayList<Data>();
public Datanode(Namenode namenode, String name) {
this.namenode = namenode;
this.name = name;
}
public Namenode getNamenode() {
return namenode;
}
public void setNamenode(Namenode namenode) {
this.namenode = namenode;
}
public List<Data> getDatas() {
return datas;
}
public void setDatas(List<Data> datas) {
this.datas = datas;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Namenode类
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
public class Namenode {
private int replicaion = 3; // 复制因子
private double safeRatio = 0.6; // 安全数据块的比例,大于此值可以启动集群
private List<Integer> metaData = new ArrayList<Integer>(); // 存放元数据
private List<Datanode> datanodes = new ArrayList<Datanode>();
public void putMetaData(int id) {
if (!metaData.contains(id)) {
metaData.add(id);
}
}
public static void main(String[] args) {
Namenode namenode = new Namenode();
System.out.println("*********启动后看下各个节点中的数据块情况*********");
for (int i = 0; i < namenode.getDatanodes().size(); i++) {
System.out.println(namenode.getDatanodes().get(i).getName() + ":");
for (int j = 0; j < namenode.getDatanodes().get(i).getDatas()
.size(); j++) {
System.out.print(namenode.getDatanodes().get(i).getDatas()
.get(j).getMsg()
+ " ");
}
System.out.println();
}
}
private void init() {
Datanode datanode1 = new Datanode(this, "datanode1");
Datanode datanode2 = new Datanode(this, "datanode2");
Data data1 = new Data(1, "data1");
Data data2 = new Data(2, "data2");
Data data3 = new Data(3, "data3");
Data data4 = new Data(4, "data4");
datanode1.getDatas().add(data1);
datanode1.getDatas().add(data2);
datanode1.getDatas().add(data3);
datanode1.getDatas().add(data4);
//让datanode2故意少几个数据块,看最后是否被复制了
datanode2.getDatas().add(data1);
datanode2.getDatas().add(data3);
datanodes.add(datanode1);
datanodes.add(datanode2);
initMetaData();
}
private void initMetaData() {
for (int i = 0; i < datanodes.size(); i++) {
for (int j = 0; j < datanodes.get(i).getDatas().size(); j++) {
int id = datanodes.get(i).getDatas().get(j).getId();
datanodes.get(i).getNamenode().putMetaData(id);
}
}
}
public Namenode() {
init();
safeMode(); // 先进入安全模式,然后启动
start();
}
private void start() {
System.out.println("hadoop started successfully...");
}
// 检查数据是否安全,有两个条件:
// 1.数据块在集群中的个数不小于replication的值,这时是安全的数据块
// 2.安全数据块的比例不小于safeRatio,则集群是安全的
private void safeMode() {
System.out.println("enter safe mode...");
System.out.println("check data safe...");
// 拿到数据数量的map,然后输出
Map<Integer, Integer> dataFrequency = dataFre();
for (Integer keys : dataFrequency.keySet()) {
System.out.println("keys: " + keys + " frequency: "
+ dataFrequency.get(keys));
}
// 将不安全的数据块记录到栈中
Stack<Integer> unsafeData = new Stack<Integer>();
for (Integer keys : dataFrequency.keySet()) {
if (dataFrequency.get(keys) < replicaion) {
System.out.println("the data" + keys + " is not safe");
unsafeData.push(keys);
}
}
// 只要数据块安全的比例小于safeRatio,就不停的复制
while ((1 - (double) unsafeData.size() / dataFrequency.size()) < safeRatio
&& unsafeData.size() >= 0) {
System.out.println("copying data" + unsafeData.peek());
while (dataFrequency.get(unsafeData.peek()) < replicaion) {
// 将数据块尽量复制到不同的节点,若所有节点节点都有这个数据块,还是达不到
// 复制因子的要求,就将它复制到datanodes中第一个节点上
if (destinaton(unsafeData.peek()) != null) {
destinaton(unsafeData.peek()).getDatas().add(
new Data(unsafeData.peek(), message(unsafeData
.peek())));
int fre = dataFrequency.get(unsafeData.peek()) + 1;
dataFrequency.put(unsafeData.peek(), fre);
} else {
datanodes
.get(0)
.getDatas()
.add(new Data(unsafeData.peek(), message(unsafeData
.peek())));
int fre = dataFrequency.get(unsafeData.peek()) + 1;
dataFrequency.put(unsafeData.peek(), fre);
}
}
System.out.println("data" + unsafeData.pop()
+ " has copyed successfully");
}
System.out.println("the cluster is safe");
System.out.println("leave safe mode");
}
/**
* 获得一个id数据的msg信息
*
* @param peek
* 数据块的id
* @return 返回它的msg信息
*/
private String message(Integer peek) {
for (int i = 0; i < datanodes.size(); i++) {
for (int j = 0; j < datanodes.get(i).getDatas().size(); j++) {
if (peek == datanodes.get(i).getDatas().get(j).getId()) {
return datanodes.get(i).getDatas().get(j).getMsg();
}
}
}
return null;
}
/**
* 决定数据块复制的目的地
*
* @param peek
* 数据块的id
* @return 返回目的地
*/
private Datanode destinaton(Integer peek) {
for (int i = 0; i < datanodes.size(); i++) {
if (!isInNode(datanodes.get(i), peek)) {
return datanodes.get(i);
}
}
return null;
}
private boolean isInNode(Datanode datanode, Integer peek) {
for (int i = 0; i < datanode.getDatas().size(); i++) {
if (peek == datanode.getDatas().get(i).getId()) {
return true;
}
}
return false;
}
/**
* 计算每个数据块的在集群中的数量
*
* @return 返回map的键为数据块的id,值为它的数量
*/
public Map<Integer, Integer> dataFre() {
Map<Integer, Integer> dataFrequency = new HashMap<Integer, Integer>();
for (int i = 0; i < metaData.size(); i++) {
for (int j = 0; j < datanodes.size(); j++) {
for (int k = 0; k < datanodes.get(j).getDatas().size(); k++) {
if (metaData.get(i) == datanodes.get(j).getDatas().get(k)
.getId()) {
if (!dataFrequency.containsKey(metaData.get(i))) {
dataFrequency.put(metaData.get(i), 1);
} else {
int fre = dataFrequency.get(metaData.get(i)) + 1;
dataFrequency.put(metaData.get(i), fre);
}
}
}
}
}
return dataFrequency;
}
public List<Integer> getMetaData() {
return metaData;
}
public void setMetaData(List<Integer> metaData) {
this.metaData = metaData;
}
public List<Datanode> getDatanodes() {
return datanodes;
}
public void setDatanodes(List<Datanode> datanodes) {
this.datanodes = datanodes;
}
}
在最小复制副本数为 2,安全的比例为0.8的情况下,运行截图如下,可以看到启动后datanode2节点中缺少的数据被复制了
若改最小副本数量为3,安全比例为0.8的情况下,根据程序会先将数据块复制到没有该数据块的节点上,若每个节点都有这个数据块还是达不到复制因子的要求时,会复制到datanodes中第一个节点中(这里是datanode1),因为这里的安全比例为0.8,四个数据块必须都是安全的,每个数据块的最小副本数量都应该不小于3,运行的截图如下:
若把最小副本数3保持不变,安全数据块的比例降到0.7,那么四个数据块中只要有三个是安全的就可以了。运行的截图如下,可以看到这里只复制了三个数据块,data1数据还是不安全的,但是不影响集群启动。