HMaster(0.90.3) Startup:
main方法:
new HMasterCommandLine(HMaster.class).doMain(args); //args为start
HMasterCommandLine: 用于启动/停止HMaster.
doMain: 运行Tool类的run方法. 继承关系:HMasterCommandLine-->ServerCommandLine-->Tool
HMasterCommandLine.run:
1. 构造CommandLine, 基本参数有三个: minServers, backup和(start/stop)
* minServers: 集群拥有的最少regionserver数
* backup: 是否是backup master
* start/stop: 启动或者停止实例
2. 如果minServers存在
设置hbase.regions.server.count.min为minServers的指, 默认为0
3. 如果backup存在
如果有backup参数,设置hbase.master.backup为true, 默认为false
4. 如果是start
startMaster()
5. 如果是stop
stopMaster()
startMaster:
hbase.cluster.distributed=false:
切换至LocalHBaseCluster(1), 启动两个JVM, 一个ZK,一个master(1)+regionserver(1).
hbase.cluster.distributed=true:
1. 创建一个HMaster实例, new HMaster(Configuration).
* 获取master的地址: ip:port, 默认port为60000, 通过hbase.master.port设置
* 获取RPC的最大handler数, 通过hbase.regionserver.handler.count设置, 默认为10.
* 创建HMaster RPCServer实例.
* 设置master的replication
* 启动rpc server的服务线程, 但是当前服务还处于不可用状态
* 修改master的mapred.task.id.(为了更好的debug log, 查看HBase MR Job运行状态)
* 连接zookeeper, 创建相关zk node(root-region-server, unassigned, rs, table)
* 创建master的统计类MasterMetrics
2. HMaster.start()
* 创建activeMasterManager对象
* 如果hbase.master.backup设置为true, sleep直到有其他master节点创建masterZNode节点.
Backup会一直sleep直到有其他master上线.
* blockUntilBecomingActiveMaster: 竞争master,如果失败则阻塞直到自己成为master
> 首先尝试创建master节点(临时节点),如果成功,自己成为master,如果失败,说明已经存在master,不管成功失败,都会在master节点上添加一个watcher.
> 如果失败, 获取当前master的节点data, 转换为HServerAddress, 如果发现地址就是自己, 说明自己做为master刚刚重启了,删除master节点.如果不是自己, 什么也不干.
> 同步clusterHasActiveMaster, 如果clusterHasActiveMaster.get(),则wait on clusterHasActiveMaster, 等待被notify. notify以后递归调用blockUntilBecomingActiveMaster.
notify由handleMasterNodeChange方法触发, 一但master的zk节点有create或者delete变化,就会触发。触发后同步clusterHasActiveMaster,并notifyAll所有wait着的backup master线程.
这里有一个有意思的地方, 如果是master自己重启, 重启以后发现zk上的master节点是自己, 然后删除节点, 这个时候会有两种情况:
1. 删除以后,先收到watcher event, 然后同步clusterHasActiveMaster, 将clusterHasActiveMaster设置为false,notifyAll. 然后blockUntilBecomingActiveMaster进入clusterHasActiveMaster同步模块,发现clusterHasActiveMaster.get()为false, 跳过wait环节,开始递归去竞争master节点.
2. 删除以后, 代码block部分先获得了clusterHasActiveMaster锁, 发现clusterHasActiveMaster.get()为true,则wait,释放锁, 然后收到watcher event,设置clusterHasActiveMaster为false, 在notifyAll唤醒wait.
有意思的地方就是网络的延迟可以造成不同的执行流程, 虽然结果都一样,但是网络快的情况下,可以少一次线程切换.
这个地方还发现有一个小bug, 就是blockUntilBecomingActiveMaster方法的返回值cleanSetOfActiveMaster, 如果是第一次进入blockUntilBecomingActiveMaster,成功成为master, 返回值为true,如果是经过一次递归以后成为master, 返回值为false. 究其原因, 是因为代码没有将递归的返回值重置给调用者. 唉..主要是这个返回值没有发挥作用,所以也没人在意吧.
// Try to become active master again now that there is no active master
blockUntilBecomingActiveMaster(); =递归改为=> cleanSetOfActiveMaster = blockUntilBecomingActiveMaster();
* finishInitialization: 顾名思义就是成为master以后需要完成一些初始化工作:
1. 创建一些master工作需要的组件:
> MasterFileSystem: 封装了master常用的一些文件系统操作,包括split log file, 删除region目录,删除table目录,删除cf目录等,检查文件系统状态等.
> HConnection: 创建与zk的连接.
> ExecutorService: 维护一个ExecutorMap, 一种Event对应一个Executor(线程池). 可以提交EventHandler来执行异步事件
> ServerManager: 管理region server信息, 维护着online region server map, 同时也维护着每个region server rpc stub.
> CatalogTracker: 同步-ROOT-和.META.的Server地址信息.
> AssignmentManager: 负责管理和分配region, 同时它也会接受zk上关于region的event,根据event来完成region的上下线,关闭打开等工作.
> LoadBalancer: 负责region在region server之间的移动, 关于balancer的策略, 可以通过hbase.regions.slop来设置load区间, 下次另启一篇来深入说明下.
> RegionServerTracker: 通过ZK的Event来跟踪online region servers, 如果有rs下线, 删除ServerManager中对应的online regions.
> ClusterStatusTracker: 维护集群状态, 通过shutdown节点来设置集群的up或者shutdown状态.
2. 启动集群,启动所有的服务线程:
> 添加shutdown节点,并设置data为master name, 表示集群启动
> 启动ExecutorService中各个Event的Executor.
> 启动LogCleaner: 每个60秒启动一次清除old hlogs.
> 启动WebApp Jetty Server: 用于通过web来访问master的状态(http://host:60010/).
> 启动RPC Server, RPC开始接受Client请求.
3. 等待region server来签到, 看策略, 这个过程应该不会太长, 如果网络抖动很厉害, 最多集群以最少regionserver数开始服务.
> 等待region server来签到, 每次等待1.5s, 最少等待时间4.5s, 如果online region servers到达minCount,并且在一次等待时间内没有新的server签到, 则不再等待.
> 统计当前online region servers总共持有的region数.
4. Split log, 分发log, 执行数据恢复
> 获取.logs目录的log目录列表, 根据目录名和online region server的名字进行对应.
> 如果有regionserver存在待恢复log目录, 则开始split log工作
> Split log/recovery的流程这个可以另起一篇来讲.
5. 分配root和meta region
> assignRootAndMeta: 检查Root region和first meta region是否被分配了, 如果没有, 分配他们.
6. 根据集群是启动还是故障恢复的情况来做清理工作
> 如果当前region数为0, 表示集群是startup, 清除所有unassignment region znode, 然后扫描meta表, 重新分配所有的region.
> 如果当前region数大于0, 表示集群是failover状态, 将处于assignment状态的节点重新assign.
7. 启动balancer定时执行线程.
8. 启动.META.定时扫描清理线程.
* loop: 循环等待, 直到Stop.
stopMaster:
Stop以后, HMaster线程结束, finally进行清理工作:
stopChores(); //停止定时线程(balancer,meta清理,logCleaner)
// Wait for all the remaining region servers to report in IFF we were
// running a cluster shutdown AND we were NOT aborting.
if (!this.abort && this.serverManager != null &&
this.serverManager.isClusterShutdown()) {
this.serverManager.letRegionServersShutdown();
}
stopServiceThreads();
// Stop services started for both backup and active masters
if (this.activeMasterManager != null) this.activeMasterManager.stop();
if (this.catalogTracker != null) this.catalogTracker.stop();
if (this.serverManager != null) this.serverManager.stop();
if (this.assignmentManager != null) this.assignmentManager.stop();
HConnectionManager.deleteConnection(this.conf, true);
this.zooKeeper.close();