【原创】大数据基础之Hadoop(1)HA实现原理
有些工作只能在一台server上进行,比如master,这时HA(High Availability)首先要求部署多个server,其次要求多个server自动选举出一个active状态server,其他server处于standby状态,只有active状态的server允许进行特定的操作;当active状态的server由于各种原因无法服务之后(比如挂了或者断网),其他standby状态的server中会马上自动选举出一个active的server来提供服务,实现服务的无缝切换;
hadoop master ha是通过zookeeper实现的,其中又分为hdfs ha(namenode的ha)和yarn ha(resourcemanager的ha),两者既有共同点,又有差别;
1 现象
1.1 hdfs ha
zookeeper path:
/hadoop-ha/${dfs.nameservices}/ActiveBreadCrumb
/hadoop-ha/${dfs.nameservices}/ActiveStandbyElectorLock
配置:
<property>
<name>ha.zookeeper.parent-znode</name>
<value>/hadoop-ha</value>
<description>
The ZooKeeper znode under which the ZK failover controller stores
its information. Note that the nameservice ID is automatically
appended to this znode, so it is not normally necessary to
configure this, even in a federated environment.
</description>
</property>
<property>
<name>dfs.nameservices</name>
<value></value>
<description>
Comma-separated list of nameservices.
</description>
</property>
1.2 yarn ha
zookeeper path:
/yarn-leader-election/${yarn.resourcemanager.cluster-id}/ActiveBreadCrumb
/yarn-leader-election/${yarn.resourcemanager.cluster-id}/ActiveStandbyElectorLock
配置:
<property>
<description>The base znode path to use for storing leader information,
when using ZooKeeper based leader election.</description>
<name>yarn.resourcemanager.ha.automatic-failover.zk-base-path</name>
<value>/yarn-leader-election</value>
</property>
<property>
<description>Name of the cluster. In a HA setting,
this is used to ensure the RM participates in leader
election for this cluster and ensures it does not affect
other clusters</description>
<name>yarn.resourcemanager.cluster-id</name>
<!--value>yarn-cluster</value-->
</property>
为什么zookeeper上有两个节点ActiveBreadCrumb和ActiveStandbyElectorLock,ActiveStandbyElectorLock是用来实际加锁的,ActiveBreadCrumb是用来做fence的;
2 代码实现
hdfs和yarn的ha最终都用到了ActiveStandbyElector,逐一来看
2.1 hdfs ha
zkfc启动命令
$HADOOP_HOME/bin/hdfs
elif [ "$COMMAND" = "zkfc" ] ; then
CLASS='org.apache.hadoop.hdfs.tools.DFSZKFailoverController'
HADOOP_OPTS="$HADOOP_OPTS $HADOOP_ZKFC_OPTS"
代码
org.apache.hadoop.hdfs.tools.DFSZKFailoverController
public static void main(String args[])
throws Exception {
if (DFSUtil.parseHelpArgument(args,
ZKFailoverController.USAGE, System.out, true)) {
System.exit(0);
} GenericOptionsParser parser = new GenericOptionsParser(
new HdfsConfiguration(), args);
DFSZKFailoverController zkfc = DFSZKFailoverController.create(
parser.getConfiguration());
int retCode = 0;
try {
retCode = zkfc.run(parser.getRemainingArgs());
} catch (Throwable t) {
LOG.fatal("Got a fatal error, exiting now", t);
}
System.exit(retCode);
}
DFSZKFailoverController.main会调用run,这里的run是父类ZKFailoverController的方法,其中会调用doRun,下面看父类:
org.apache.hadoop.ha.ZKFailoverController
private static final String ZK_PARENT_ZNODE_KEY = "ha.zookeeper.parent-znode"; static final String ZK_PARENT_ZNODE_DEFAULT = "/hadoop-ha"; private int doRun(String[] args)
throws HadoopIllegalArgumentException, IOException, InterruptedException {
try {
initZK();
} catch (KeeperException ke) {
LOG.fatal("Unable to start failover controller. Unable to connect "
+ "to ZooKeeper quorum at " + zkQuorum + ". Please check the "
+ "configured value for " + ZK_QUORUM_KEY + " and ensure that "
+ "ZooKeeper is running.");
return ERR_CODE_NO_ZK;
}
if (args.length > 0) {
if ("-formatZK".equals(args[0])) {
boolean force = false;
boolean interactive = true;
for (int i = 1; i < args.length; i++) {
if ("-force".equals(args[i])) {
force = true;
} else if ("-nonInteractive".equals(args[i])) {
interactive = false;
} else {
badArg(args[i]);
}
}
return formatZK(force, interactive);
} else {
badArg(args[0]);
}
} if (!elector.parentZNodeExists()) {
LOG.fatal("Unable to start failover controller. "
+ "Parent znode does not exist.\n"
+ "Run with -formatZK flag to initialize ZooKeeper.");
return ERR_CODE_NO_PARENT_ZNODE;
} try {
localTarget.checkFencingConfigured();
} catch (BadFencingConfigurationException e) {
LOG.fatal("Fencing is not configured for " + localTarget + ".\n" +
"You must configure a fencing method before using automatic " +
"failover.", e);
return ERR_CODE_NO_FENCER;
} initRPC();
initHM();
startRPC();
try {
mainLoop();
} finally {
rpcServer.stopAndJoin(); elector.quitElection(true);
healthMonitor.shutdown();
healthMonitor.join();
}
return 0;
} private void initZK() throws HadoopIllegalArgumentException, IOException,
KeeperException {
...
elector = new ActiveStandbyElector(zkQuorum,
zkTimeout, getParentZnode(), zkAcls, zkAuths,
new ElectorCallbacks(), maxRetryNum);
} private String getParentZnode() {
String znode = conf.get(ZK_PARENT_ZNODE_KEY,
ZK_PARENT_ZNODE_DEFAULT);
if (!znode.endsWith("/")) {
znode += "/";
}
return znode + getScopeInsideParentNode();
} class ElectorCallbacks implements ActiveStandbyElectorCallback {
@Override
public void becomeActive() throws ServiceFailedException {
ZKFailoverController.this.becomeActive();
} @Override
public void becomeStandby() {
ZKFailoverController.this.becomeStandby();
} @Override
public void enterNeutralMode() {
} @Override
public void notifyFatalError(String errorMessage) {
fatalError(errorMessage);
} @Override
public void fenceOldActive(byte[] data) {
ZKFailoverController.this.fenceOldActive(data);
} @Override
public String toString() {
synchronized (ZKFailoverController.this) {
return "Elector callbacks for " + localTarget;
}
}
}
doRun中会调用几个方法,最重要的两个是initZK和initHM:
在initZK中会通过getParentZnode创建zk路径,同时创建ActiveStandbyElector,这里最重要的是把内部类ElectorCallbacks的对象传到ActiveStandbyElector,后续各种zk状态都是通过ActiveStandbyElector->ElectorCallbacks->ZKFailoverController这个调用链传递的,最终实现状态变更,比如becomeActive,becomeStandby等;
initZK中只是确定了zk路径以及各种回调函数,还没有实际的创建操作,具体的操作在initHM中,下面看initHM:
org.apache.hadoop.ha.ZKFailoverController
private void initHM() {
healthMonitor = new HealthMonitor(conf, localTarget);
healthMonitor.addCallback(new HealthCallbacks());
healthMonitor.addServiceStateCallback(new ServiceStateCallBacks());
healthMonitor.start();
}
initHM中会创建HealthMonitor,传入HealthCallbacks,然后启动HealthMonitor,下面看HealthMonitor:
org.apache.hadoop.ha.HealthMonitor
void start() {
daemon.start();
}
private void doHealthChecks() throws InterruptedException {
while (shouldRun) {
HAServiceStatus status = null;
boolean healthy = false;
try {
status = proxy.getServiceStatus();
proxy.monitorHealth();
healthy = true;
} catch (HealthCheckFailedException e) {
LOG.warn("Service health check failed for " + targetToMonitor
+ ": " + e.getMessage());
enterState(State.SERVICE_UNHEALTHY);
} catch (Throwable t) {
LOG.warn("Transport-level exception trying to monitor health of " +
targetToMonitor + ": " + t.getLocalizedMessage());
RPC.stopProxy(proxy);
proxy = null;
enterState(State.SERVICE_NOT_RESPONDING);
Thread.sleep(sleepAfterDisconnectMillis);
return;
}
if (status != null) {
setLastServiceStatus(status);
}
if (healthy) {
enterState(State.SERVICE_HEALTHY);
}
Thread.sleep(checkIntervalMillis);
}
}
private synchronized void enterState(State newState) {
if (newState != state) {
LOG.info("Entering state " + newState);
state = newState;
synchronized (callbacks) {
for (Callback cb : callbacks) {
cb.enteredState(newState);
}
}
}
}
HealthMonitor.start会启动内部的MonitorDaemon线程,而MonitorDaemon线程中中会循环调用HealthMonitor.doHealthChecks,doHealthChecks会根据各种状态变化调用enterState,而enterState会迭代回调所有的callbacks,这是一个Observer模式,重点在callback上,即HealthCallbacks;
先看MonitorDaemon线程:
org.apache.hadoop.ha.HealthMonitor.MonitorDaemon
public void run() {
while (shouldRun) {
try {
loopUntilConnected();
doHealthChecks();
} catch (InterruptedException ie) {
Preconditions.checkState(!shouldRun,
"Interrupted but still supposed to run");
}
}
}
再看HealthCallbacks:
org.apache.hadoop.ha.ZKFailoverController.HealthCallbacks
class HealthCallbacks implements HealthMonitor.Callback {
@Override
public void enteredState(HealthMonitor.State newState) {
setLastHealthState(newState);
recheckElectability();
}
}
这里会调用到ZKFailoverController.recheckElectability
org.apache.hadoop.ha.ZKFailoverController
private void recheckElectability() {
// Maintain lock ordering of elector -> ZKFC
synchronized (elector) {
synchronized (this) {
boolean healthy = lastHealthState == State.SERVICE_HEALTHY;
long remainingDelay = delayJoiningUntilNanotime - System.nanoTime();
if (remainingDelay > 0) {
if (healthy) {
LOG.info("Would have joined master election, but this node is " +
"prohibited from doing so for " +
TimeUnit.NANOSECONDS.toMillis(remainingDelay) + " more ms");
}
scheduleRecheck(remainingDelay);
return;
}
switch (lastHealthState) {
case SERVICE_HEALTHY:
elector.joinElection(targetToData(localTarget));
if (quitElectionOnBadState) {
quitElectionOnBadState = false;
}
break;
case INITIALIZING:
LOG.info("Ensuring that " + localTarget + " does not " +
"participate in active master election");
elector.quitElection(false);
serviceState = HAServiceState.INITIALIZING;
break;
case SERVICE_UNHEALTHY:
case SERVICE_NOT_RESPONDING:
LOG.info("Quitting master election for " + localTarget +
" and marking that fencing is necessary");
elector.quitElection(true);
serviceState = HAServiceState.INITIALIZING;
break;
case HEALTH_MONITOR_FAILED:
fatalError("Health monitor failed!");
break;
default:
throw new IllegalArgumentException("Unhandled state:" + lastHealthState);
}
}
}
}
在health的情况下会调用ActiveStandbyElector.joinElection,下面看ActiveStandbyElector:
org.apache.hadoop.ha.ActiveStandbyElector
public class ActiveStandbyElector implements StatCallback, StringCallback {
public ActiveStandbyElector(String zookeeperHostPorts,
int zookeeperSessionTimeout, String parentZnodeName, List<ACL> acl,
List<ZKAuthInfo> authInfo,
ActiveStandbyElectorCallback app, int maxRetryNum) throws IOException,
HadoopIllegalArgumentException, KeeperException {
...
znodeWorkingDir = parentZnodeName;
zkLockFilePath = znodeWorkingDir + "/" + LOCK_FILENAME;
zkBreadCrumbPath = znodeWorkingDir + "/" + BREADCRUMB_FILENAME;
...
public synchronized void joinElection(byte[] data)
throws HadoopIllegalArgumentException {
if (data == null) {
throw new HadoopIllegalArgumentException("data cannot be null");
}
if (wantToBeInElection) {
LOG.info("Already in election. Not re-connecting.");
return;
}
appData = new byte[data.length];
System.arraycopy(data, 0, appData, 0, data.length);
LOG.debug("Attempting active election for " + this);
joinElectionInternal();
}
private void joinElectionInternal() {
Preconditions.checkState(appData != null,
"trying to join election without any app data");
if (zkClient == null) {
if (!reEstablishSession()) {
fatalError("Failed to reEstablish connection with ZooKeeper");
return;
}
}
createRetryCount = 0;
wantToBeInElection = true;
createLockNodeAsync();
}
private void createLockNodeAsync() {
zkClient.create(zkLockFilePath, appData, zkAcl, CreateMode.EPHEMERAL,
this, zkClient);
}
ActiveStandbyElector实现了两个zookeeper的callback接口StatCallback和StringCallback,调用过程为joinElection->joinElectionInternal->createLockNodeAsync,最终会调用ZooKeeper.create异步方法,同时把自己作为callback传进去,这样zookeeper后续的变更都会回调ActiveStandbyElector.processResult,而processResult中会回调ElectorCallbacks,至此整个流程打通。
zookeeper的StringCallback接口如下:
org.apache.zookeeper.AsyncCallback.StringCallback
interface StringCallback extends AsyncCallback {
public void processResult(int rc, String path, Object ctx, String name);
}
2.2 yarn ha
org.apache.hadoop.yarn.conf.YarnConfiguration
public static final String AUTO_FAILOVER_ZK_BASE_PATH =
AUTO_FAILOVER_PREFIX + "zk-base-path";
public static final String DEFAULT_AUTO_FAILOVER_ZK_BASE_PATH =
"/yarn-leader-election";
这里可以看到配置
org.apache.hadoop.yarn.server.resourcemanager.EmbeddedElectorService
protected void serviceInit(Configuration conf)
throws Exception {
...
String zkBasePath = conf.get(YarnConfiguration.AUTO_FAILOVER_ZK_BASE_PATH,
YarnConfiguration.DEFAULT_AUTO_FAILOVER_ZK_BASE_PATH);
String electionZNode = zkBasePath + "/" + clusterId;
...
elector = new ActiveStandbyElector(zkQuorum, (int) zkSessionTimeout,
electionZNode, zkAcls, zkAuths, this, maxRetryNum);
... @Override
protected void serviceStart() throws Exception {
elector.joinElection(localActiveNodeInfo);
super.serviceStart();
}
过程和上述ZKFailoverController差不多,EmbeddedElectorService.serviceInit中会创建zk路径同时创建ActiveStandbyElector,然后在serviceStart中会调用ActiveStandbyElector.joinElection
【原创】大数据基础之Hadoop(1)HA实现原理的更多相关文章
- 学习大数据基础框架hadoop需要什么基础
什么是大数据?进入本世纪以来,尤其是2010年之后,随着互联网特别是移动互联网的发展,数据的增长呈爆炸趋势,已经很难估计全世界的电子设备中存储的数据到底有多少,描述数据系统的数据量的计量单位从MB(1 ...
- 【原创】大数据基础之Hadoop(3)yarn数据收集与监控
yarn常用rest api 1 metrics # curl http://localhost:8088/ws/v1/cluster/metrics The cluster metrics reso ...
- 【原创】大数据基础之Hadoop(2)hdfs和yarn最简绿色部署
环境:3结点集群 192.168.0.1192.168.0.2192.168.0.3 1 配置root用户服务期间免密登录 参考:https://www.cnblogs.com/barneywill/ ...
- 大数据基础总结---MapReduce和YARN技术原理
Map Reduce和YARN技术原理 学习目标 熟悉MapReduce和YARN是什么 掌握MapReduce使用的场景及其原理 掌握MapReduce和YARN功能与架构 熟悉YARN的新特性 M ...
- 【大数据技术】Hadoop三大组件架构原理(HDFS-YARN-MapReduce)
目前,Hadoop还只是数据仓库产品的一个补充,和数据仓库一起构建混搭架构为上层应用联合提供服务. Hadoop集群具体来说包含两个集群:HDFS集群和YARN集群,两者逻辑上分离,但物理上常在一起. ...
- 【原创】大数据基础之Zookeeper(2)源代码解析
核心枚举 public enum ServerState { LOOKING, FOLLOWING, LEADING, OBSERVING; } zookeeper服务器状态:刚启动LOOKING,f ...
- 一篇了解大数据架构及Hadoop生态圈
一篇了解大数据架构及Hadoop生态圈 阅读建议,有一定基础的阅读顺序为1,2,3,4节,没有基础的阅读顺序为2,3,4,1节. 第一节 集群规划 大数据集群规划(以CDH集群为例),参考链接: ht ...
- 大数据时代之hadoop(五):hadoop 分布式计算框架(MapReduce)
大数据时代之hadoop(一):hadoop安装 大数据时代之hadoop(二):hadoop脚本解析 大数据时代之hadoop(三):hadoop数据流(生命周期) 大数据时代之hadoop(四): ...
- 大数据测试之初识Hadoop
大数据测试之初识Hadoop POPTEST老李认为测试开发工程师是面向测试的开发,也就是说,写代码就是为完成测试任务服务的,写自动化测试(性能自动化,功能自动化,安全自动化,接口自动化等等)的cas ...
随机推荐
- cmd执行超大sql文件
osql -S 127.0.0.1 -U sa -P 123456 -i d:\test.sql osql为SQL Server的命令,要在cmd中执行该命令,一般安装完SQL Server后该命令对 ...
- python基础概念(转)
基础回顾: 1.集合 集合有2个重要作用:关系测试(并集,差集,交集)和去重. 2.文件编码 2.7上默认文件编码是ASCII码,因为不支持中文,就出了GB2312,在2.7上要支持中文就必须申明文件 ...
- Linux命令(五)创建文件或修改文件时间 touch
Linux中 touch 命令可以改变文档或目录时间, 包括存取时间或更改时间, 也可以用于创建新文件. 命令格式: touch [选项] [参数] 选项: -a 只更改文件的读取时间. -m ...
- Linux基础操作文件等基础操作
作业一: 1) 将用户信息数据库文件和组信息数据库文件纵向合并为一个文件/1.txt(覆盖) [root@bogon /]# cd /etc [root@bogon etc]# cat passwd ...
- sublime 官方正版,自己用的插件配置,最轻量级安装流程
到了一家新公司,新的办公电脑,移动工作站哦,配置很酷.需要自己安装编码环境,node.js(http-server)是必须要装的,编辑器个人比较喜欢sublime,现在归纳一下配置流程,ps:本人有点 ...
- Shell命令-系统信息及显示之stat、du
文件及内容处理 - stat.du 1. stat:显示inode内容 stat命令的功能说明 stat 命令用于显示 inode 内容.stat 以文字的格式来显示 inode 的内容. stat命 ...
- Django模板语言进阶
一.母板 1.什么情况下使用母版 当多个页面的大部分内容都一样的时候,我们可以把相同的部分提取出来,放到一个单独的母版HTML文件中 然后在母版中定义需要被替换的block 例如:母板页面 <! ...
- 根据字段获取DataTable包含某个值的数据
dt.Select("身份证号='" + list[i].PersonalId + "' and 培训完成日期 like '" + year + "% ...
- <Android基础>(三) UI开发 Part 2 ListView
ListView 1)ListView的简单用法 2)定制ListView界面 3)提升ListView的运行效率 4)ListView的点击事件 3.5 ListView 3.5.1 ListVie ...
- Java【初识篇】语言概述
什么是计算机语言 语言:是人与人之间用于沟通的一种方式.例如:中国人与中国人用普通话沟通.而中国人要和英国人交流,就要学习英语.计算机语言(编程语言):人与计算机交流的方式.如果人要与计算机交流,那么 ...