Hbase的一个很大的优点就是可扩展性比较强,可以线性扩展,加机器就行,而且机器的配置也不需要太好,就是用大量廉价的机器来替代昂贵的小型机,但是由于大量的相对廉价的机器,由于网络硬盘等各种各样的原因,机器不可避免的会挂掉,那么HBase在面对这种问题时如何处理呢?
首先来看HBase的RS在哪几种情况下会down掉:
1. 集群关闭;
2. stop regionserver 关闭当前RS,当然也可启动当前RS
3. 线程kill掉,包括kill + 线程号 and kill -9 + 线程号
4. RS在运行过程中产生自身难以解决的problem,此时它会自己abort自己。
而按照HBase对这几种down掉的处理情况又可以吧上述分为3类:
第一种是kill -9 类型的此时rs直接退出,直接中断任何运行中的线程,从日志中我们也可以发现没有任何日志记录
第二种是abortRequested类型的,这种是由于RS运行期间出错导致RS自己abort自己,这个过程会做很多收尾工作,关闭线程,关闭storefile的reader对象,关闭hlog等等
第三种就是stop类型的,包括kill 线程和stop regionserver和集群关闭,与第二种比较相似,而且会把此时内存的数据刷新到磁盘以后,并把hlog移到.oldlogs
在0.90版本上,第三种的关闭存在一个bug会造成数据丢失。
我们来看看第三中是如何处理的:
this.leases.closeAfterLeasesExpire();//释放所有的Lease后,关闭Leases线程 this.server.stop();//close socket和handler if (this.splitLogWorker != null) { splitLogWorker.stop();//close splitlog 线程 } if (this.infoServer != null) { LOG.info("Stopping infoServer"); try { this.infoServer.stop();//close http端的接口,主要是REST应用 } catch (Exception e) { e.printStackTrace(); } } // Send cache a shutdown. LruBlockCache c = (LruBlockCache) StoreFile.getBlockCache(this.conf); if (c != null) { c.shutdown();// close 每5分钟一次的blockcache信息 } // Send interrupts to wake up threads if sleeping so they notice shutdown. // TODO: Should we check they are alive? If OOME could have exited already if (this.cacheFlusher != null) this.cacheFlusher.interruptIfNecessary();//关闭memstoreflusher线程,默认是超过64M会刷一次磁盘 if (this.compactSplitThread != null) this.compactSplitThread.interruptIfNecessary();//关闭split与compact线程 if (this.hlogRoller != null) this.hlogRoller.interruptIfNecessary();//close hlogRoller,每24小时清理一次 if (this.majorCompactionChecker != null) this.majorCompactionChecker.interrupt();//close majoreCompactorChecker,默认24小时做一次major compact if (this.killed) {//kill - 9 // Just skip out w/o closing regions. } else if (abortRequested) {RS自己abort if (this.fsOk) { closeAllRegions(abortRequested); // Don't leave any open file handles closeWAL(false); } LOG.info("aborting server at: " + this.serverInfo.getServerName()); } else {//stop closeAllRegions(abortRequested); closeWAL(true); closeAllScanners(); LOG.info("stopping server at: " + this.serverInfo.getServerName()); } // Interrupt catalog tracker here in case any regions being opened out in // handlers are stuck waiting on meta or root. if (this.catalogTracker != null) this.catalogTracker.stop(); if (this.fsOk) waitOnAllRegionsToClose(); // Make sure the proxy is down. if (this.hbaseMaster != null) { HBaseRPC.stopProxy(this.hbaseMaster); this.hbaseMaster = null; } this.leases.close(); HConnectionManager.deleteConnection(conf, true); this.zooKeeper.close(); if (!killed) { join(); } LOG.info(Thread.currentThread().getName() + " exiting");
在stop reginserver的过程当中会
else {//stop closeAllRegions(abortRequested); closeWAL(true); closeAllScanners(); LOG.info("stopping server at: " + this.serverInfo.getServerName()); }
首先关闭所有的Region,这里的关闭Region是启动新的线程去关闭Region,正常好像是3个?,若存在meta或是root则分别另启一个线程处理。
然后closeWAL,close Hlog的writer,并把.logs移动到.oldlogs目录下,然后删除.logs的目录
最后关闭所有的scanners,防止下一次打开的时候出错
在上述过程中,由于close region是另起线程进行处理的,那么就有可能存在这样的情况,region还未关闭结束如正在flush,而此时closeWal已经结束并且把logs目录删除了,这就可能会造成data loss ,应为在flush的时候比如kill -9了那此时memstore的数据丢失了,而logs也没有数据 ,故无法恢复从而造成data loss
解决方案是 等到所有的region close 以后再做close Wal操作
RS在启动的时候会在ZK下的RS目录注册自己的节点,而master会监听到这个节点,当RS挂掉以后,ZK上的该节点delete掉,而Master会捕捉到这个事件调用nodedeleted来处理
下一篇介绍一下master观察到zk上rs节点delete以后的操作