[HBase]Region assignment

转载自:http://iwinit.iteye.com/blog/1818897

我们创建表t1,列族c1,hbase.root目录为/new。当创建空表时,系统会自动生成一个空region,我们以这个region分配过程看下Region是如何在HMaster和Region server(以下简称rs)中创建的。大致过程如下:

1.HMaster指定分配计划,一个region只会分配给一个rs,多个rs均匀分配

2.多个rs并发执行assiagnment操作

3.先在zk的/hbase/assiangment目录下创建region节点,状态为‘offline’

4.RPC对应rs,请求分配region

5.master端开始等待所有region都被分配,通过zk的节点状态通信

6.rs端收到请求,执行异步OpenRegion操作

7.rs先把zk节点状态改为'opening'

8.rs执行open region操作,并初始化region,主要是创建region的HDFS目录,初始化Store

9.rs修改meta表中region对应的记录信息

10.rs修改zk节点中的状态为'opened'

11.master收到'opened'信息,认为该region已经assiagnment成功

12.所有region都成功后,master认为region批量创建成功

大概类图 

在HMaster端提供了BulkAssigner,用来批量分配region,默认采用随即均匀分配,分配过程是一个rpc调用

 

Java代码   收藏代码
  1. public boolean bulkAssign(boolean sync) throws InterruptedException,  
  2.       IOException {  
  3.     boolean result = false;  
  4.     ThreadFactoryBuilder builder = new ThreadFactoryBuilder();  
  5.     builder.setDaemon(true);  
  6.     builder.setNameFormat(getThreadNamePrefix() + "-%1$d");  
  7.     builder.setUncaughtExceptionHandler(getUncaughtExceptionHandler());  
  8.     int threadCount = getThreadCount();  
  9.     java.util.concurrent.ExecutorService pool =  
  10.       Executors.newFixedThreadPool(threadCount, builder.build());  
  11.     try {  
  12.     //提交任务,任务为SingleServerBulkAssigner  
  13.       populatePool(pool);  
  14.       // How long to wait on empty regions-in-transition.  If we timeout, the  
  15.       // RIT monitor should do fixup.  
  16.     //等待  
  17.       if (sync) result = waitUntilDone(getTimeoutOnRIT());  
  18.     } finally {  
  19.       // We're done with the pool.  It'll exit when its done all in queue.  
  20.       pool.shutdown();  
  21.     }  
  22.     return result;  
  23.   }  

等待过程

 

 

Java代码   收藏代码
  1.  boolean waitUntilNoRegionsInTransition(final long timeout, Set<HRegionInfo> regions)  
  2.  throws InterruptedException {  
  3.    // Blocks until there are no regions in transition.  
  4. //如果带处理的region有一个还在事务列表中,则继续等  
  5. //超时时间由hbase.bulk.assignment.waiton.empty.rit设置,默认5分钟  
  6.    long startTime = System.currentTimeMillis();  
  7.    long remaining = timeout;  
  8.    boolean stillInTransition = true;  
  9.    synchronized (regionsInTransition) {  
  10.      while (regionsInTransition.size() > 0 && !this.master.isStopped() &&  
  11.          remaining > 0 && stillInTransition) {  
  12.        int count = 0;  
  13.        for (RegionState rs : regionsInTransition.values()) {  
  14.          if (regions.contains(rs.getRegion())) {  
  15.            count++;  
  16.            break;  
  17.          }  
  18.        }  
  19.        if (count == 0) {  
  20.          stillInTransition = false;  
  21.          break;  
  22.        }  
  23.        regionsInTransition.wait(remaining);  
  24.        remaining = timeout - (System.currentTimeMillis() - startTime);  
  25.      }  
  26.    }  
  27.    return stillInTransition;  
  28.  }  

 AssignmentManager提供了assign(final ServerName destination,final List<HRegionInfo> regions)给每个rs批量assign region

 

 

Java代码   收藏代码
  1. void assign(final ServerName destination,  
  2.       final List<HRegionInfo> regions) {  
  3.     ....  
  4.     //强制初始化状态为offline  
  5.     List<RegionState> states = new ArrayList<RegionState>(regions.size());  
  6.     synchronized (this.regionsInTransition) {  
  7.       for (HRegionInfo region: regions) {  
  8.         states.add(forceRegionStateToOffline(region));  
  9.       }  
  10.     }  
  11.     .....  
  12.       
  13.     // Presumption is that only this thread will be updating the state at this  
  14.     // time; i.e. handlers on backend won't be trying to set it to OPEN, etc.  
  15.     //给每个带分配的region创建zk的节点,目录为/hbase/unassigned,并初始化状态为offline。  
  16.     //节点创建成功后,在callback中调用zk的exist,设置watcher,在exist操作的callback中将region的状态设为‘PENDING_OPEN’,递增counter  
  17.     //所有region都需要设置成功  
  18.     AtomicInteger counter = new AtomicInteger(0);  
  19.     CreateUnassignedAsyncCallback cb =  
  20.       new CreateUnassignedAsyncCallback(this.watcher, destination, counter);  
  21.     for (RegionState state: states) {  
  22.       if (!asyncSetOfflineInZooKeeper(state, destination, cb, state)) {  
  23.         return;  
  24.       }  
  25.     }  
  26.     // Wait until all unassigned nodes have been put up and watchers set.  
  27.     int total = regions.size();  
  28.     for (int oldCounter = 0true;) {  
  29.       int count = counter.get();  
  30.       if (oldCounter != count) {  
  31.         LOG.info(destination.toString() + " unassigned znodes=" + count +  
  32.           " of total=" + total);  
  33.         oldCounter = count;  
  34.       }  
  35.       if (count == total) break;  
  36.       Threads.sleep(1);  
  37.     }  
  38.     // Move on to open regions.  
  39.     try {  
  40.       // Send OPEN RPC. If it fails on a IOE or RemoteException, the  
  41.       // TimeoutMonitor will pick up the pieces.  
  42.     //发送RPC请求给rs,如果rpc失败,可重试,最大超时时间60s  
  43.       long maxWaitTime = System.currentTimeMillis() +  
  44.         this.master.getConfiguration().  
  45.           getLong("hbase.regionserver.rpc.startup.waittime"60000);  
  46.       while (!this.master.isStopped()) {  
  47.         try {  
  48.           this.serverManager.sendRegionOpen(destination, regions);  
  49.           break;  
  50.         } catch (RemoteException e) {  
  51.           IOException decodedException = e.unwrapRemoteException();  
  52.           if (decodedException instanceof RegionServerStoppedException) {  
  53.             LOG.warn("The region server was shut down, ", decodedException);  
  54.             // No need to retry, the region server is a goner.  
  55.             return;  
  56.           } else if (decodedException instanceof ServerNotRunningYetException) {  
  57.             // This is the one exception to retry.  For all else we should just fail  
  58.             // the startup.  
  59.             long now = System.currentTimeMillis();  
  60.             if (now > maxWaitTime) throw e;  
  61.             LOG.debug("Server is not yet up; waiting up to " +  
  62.                 (maxWaitTime - now) + "ms", e);  
  63.             Thread.sleep(1000);  
  64.           }  
  65.   
  66.           throw decodedException;  
  67.         }  
  68.       }  
  69.     }   
  70.     .......  
  71.   }  

 rs的RPC接口HRegionInterface.openRegions(final List<HRegionInfo> regions),rs初始化region,并通过zk状态告知master是否成功,这是一个异步过程。

 

用户表open region为OpenRegionHandler,处理

 

Java代码   收藏代码
  1. public void process() throws IOException {  
  2.     try {  
  3.      .....  
  4.   
  5.       // If fails, just return.  Someone stole the region from under us.  
  6.       // Calling transitionZookeeperOfflineToOpening initalizes this.version.  
  7.     //将/hbase/unassigned下的节点状态从‘offline’改成‘opening’  
  8.       if (!transitionZookeeperOfflineToOpening(encodedName,  
  9.           versionOfOfflineNode)) {  
  10.         LOG.warn("Region was hijacked? It no longer exists, encodedName=" +  
  11.           encodedName);  
  12.         return;  
  13.       }  
  14.   
  15.       // Open region.  After a successful open, failures in subsequent  
  16.       // processing needs to do a close as part of cleanup.  
  17.     //执行open操作  
  18.       region = openRegion();  
  19.       if (region == null) {  
  20.         tryTransitionToFailedOpen(regionInfo);  
  21.         return;  
  22.       }  
  23.       boolean failed = true;  
  24.     //open成功后,先更新下zk中的节点时间,再修改meta表中的region记录  
  25.     //主要是修改meta表中的serverstartcode和server列  
  26.       if (tickleOpening("post_region_open")) {  
  27.         if (updateMeta(region)) {  
  28.           failed = false;  
  29.         }  
  30.       }  
  31.     //如果修改失败,或者进入stop阶段,关闭region,将zk节点状态设为‘FAILED_OPEN’  
  32.       if (failed || this.server.isStopped() ||  
  33.           this.rsServices.isStopping()) {  
  34.         cleanupFailedOpen(region);  
  35.         tryTransitionToFailedOpen(regionInfo);  
  36.         return;  
  37.       }  
  38.     //将zk节点状态设为‘OPENED’,如果失败,关闭region  
  39.       if (!transitionToOpened(region)) {  
  40.         // If we fail to transition to opened, it's because of one of two cases:  
  41.         //    (a) we lost our ZK lease  
  42.         // OR (b) someone else opened the region before us  
  43.         // In either case, we don't need to transition to FAILED_OPEN state.  
  44.         // In case (a), the Master will process us as a dead server. In case  
  45.         // (b) the region is already being handled elsewhere anyway.  
  46.         cleanupFailedOpen(region);  
  47.         return;  
  48.       }  
  49.       // Successful region open, and add it to OnlineRegions  
  50.     //添加到online列表  
  51.       this.rsServices.addToOnlineRegions(region);  
  52.   
  53.       .....  
  54.   }  

 Region初始化

 

 

Java代码   收藏代码
  1. private long initializeRegionInternals(final CancelableProgressable reporter,  
  2.       MonitoredTask status) throws IOException, UnsupportedEncodingException {  
  3.     .....  
  4.   
  5.     // Write HRI to a file in case we need to recover .META.  
  6.     status.setStatus("Writing region info on filesystem");  
  7.     //写入.regioninfo文件,内容是HRegionInfo序列化的内容,region的元信息  
  8.     checkRegioninfoOnFilesystem();  
  9.   
  10.     // Remove temporary data left over from old regions  
  11.     status.setStatus("Cleaning up temporary data from old regions");  
  12.     //.tmp目录删除  
  13.     cleanupTmpDir();  
  14.   
  15.     // Load in all the HStores.  
  16.     //  
  17.     // Context: During replay we want to ensure that we do not lose any data. So, we  
  18.     // have to be conservative in how we replay logs. For each store, we calculate  
  19.     // the maxSeqId up to which the store was flushed. And, skip the edits which  
  20.     // is equal to or lower than maxSeqId for each store.  
  21.     //每个family启动一个线程加载store  
  22.     //等全部store都加载后,取最大的seqId和memstoreTS  
  23.     Map<byte[], Long> maxSeqIdInStores = new TreeMap<byte[], Long>(  
  24.         Bytes.BYTES_COMPARATOR);  
  25.     long maxSeqId = -1;  
  26.     // initialized to -1 so that we pick up MemstoreTS from column families  
  27.     long maxMemstoreTS = -1;  
  28.   
  29.     if (this.htableDescriptor != null &&  
  30.         !htableDescriptor.getFamilies().isEmpty()) {  
  31.       // initialize the thread pool for opening stores in parallel.  
  32.       ThreadPoolExecutor storeOpenerThreadPool =  
  33.         getStoreOpenAndCloseThreadPool(  
  34.           "StoreOpenerThread-" + this.regionInfo.getRegionNameAsString());  
  35.       CompletionService<Store> completionService =  
  36.         new ExecutorCompletionService<Store>(storeOpenerThreadPool);  
  37.   
  38.       // initialize each store in parallel  
  39.       for (final HColumnDescriptor family : htableDescriptor.getFamilies()) {  
  40.         status.setStatus("Instantiating store for column family " + family);  
  41.         completionService.submit(new Callable<Store>() {  
  42.           public Store call() throws IOException {  
  43.             return instantiateHStore(tableDir, family);  
  44.           }  
  45.         });  
  46.       }  
  47.       try {  
  48.         for (int i = 0; i < htableDescriptor.getFamilies().size(); i++) {  
  49.           Future<Store> future = completionService.take();  
  50.           Store store = future.get();  
  51.   
  52.           this.stores.put(store.getColumnFamilyName().getBytes(), store);  
  53.           long storeSeqId = store.getMaxSequenceId();  
  54.           maxSeqIdInStores.put(store.getColumnFamilyName().getBytes(),  
  55.               storeSeqId);  
  56.           if (maxSeqId == -1 || storeSeqId > maxSeqId) {  
  57.             maxSeqId = storeSeqId;  
  58.           }  
  59.           long maxStoreMemstoreTS = store.getMaxMemstoreTS();  
  60.           if (maxStoreMemstoreTS > maxMemstoreTS) {  
  61.             maxMemstoreTS = maxStoreMemstoreTS;  
  62.           }  
  63.         }  
  64.       ......  
  65.     }  
  66.     mvcc.initialize(maxMemstoreTS + 1);  
  67.     // Recover any edits if available.  
  68.     maxSeqId = Math.max(maxSeqId, replayRecoveredEditsIfAny(  
  69.         this.regiondir, maxSeqIdInStores, reporter, status));  
  70.   
  71.     .......  
  72.    
  73.     this.lastFlushTime = EnvironmentEdgeManager.currentTimeMillis();  
  74.     // Use maximum of log sequenceid or that which was found in stores  
  75.     // (particularly if no recovered edits, seqid will be -1).  
  76.     //递增seqid  
  77.     long nextSeqid = maxSeqId + 1;  
  78.     ......  
  79.     return nextSeqid;  
  80.   }  

 rs端的处理就是这些,master端通过zk的watcher监听rs端的region状态修改,AssignmentManager的nodeDataChanged方法就是用来处理这个的。

 

 

Java代码   收藏代码
  1.  public void nodeDataChanged(String path) {  
  2.    if(path.startsWith(watcher.assignmentZNode)) {  
  3.      try {  
  4.        Stat stat = new Stat();  
  5. //当data变化时,获取data,然后再设置watcher,下次继续处理  
  6.        RegionTransitionData data = ZKAssign.getDataAndWatch(watcher, path, stat);  
  7.        if (data == null) {  
  8.          return;  
  9.        }  
  10.        handleRegion(data, stat.getVersion());  
  11.      } catch (KeeperException e) {  
  12.        master.abort("Unexpected ZK exception reading unassigned node data", e);  
  13.      }  
  14.    }  
  15.  }  

 当rs把region状态设为opening时

 

 

Java代码   收藏代码
  1. case RS_ZK_REGION_OPENING:  
  2.           .....  
  3.           // Transition to OPENING (or update stamp if already OPENING)  
  4.     //更新时间  
  5.           regionState.update(RegionState.State.OPENING,  
  6.               data.getStamp(), data.getOrigin());  
  7.           break;  

 当rs把region状态设为‘opened‘时

 

 

Java代码   收藏代码
  1. case RS_ZK_REGION_OPENED:  
  2.           ......  
  3.           // Handle OPENED by removing from transition and deleted zk node  
  4.     //内存状态改为open  
  5.           regionState.update(RegionState.State.OPEN,  
  6.               data.getStamp(), data.getOrigin());  
  7.           this.executorService.submit(  
  8.             new OpenedRegionHandler(master, this, regionState.getRegion(),  
  9.               data.getOrigin(), expectedVersion));  
  10.           break;  

 OpenedRegionHandler主要是删除之前创建的/hbase/unassigned下的region节点

Java代码   收藏代码
  1. public void process() {  
  2.   // Code to defend against case where we get SPLIT before region open  
  3.   // processing completes; temporary till we make SPLITs go via zk -- 0.92.  
  4.   RegionState regionState = this.assignmentManager.isRegionInTransition(regionInfo);  
  5.   boolean openedNodeDeleted = false;  
  6.   if (regionState != null  
  7.       && regionState.getState().equals(RegionState.State.OPEN)) {  
  8.     openedNodeDeleted = deleteOpenedNode(expectedVersion);  
  9.     if (!openedNodeDeleted) {  
  10.       LOG.error("The znode of region " + regionInfo.getRegionNameAsString()  
  11.           + " could not be deleted.");  
  12.     }  
  13.   }   
  14. .....  
  15. }  

 节点删除后,又有zk通知,AssignmentManager的nodeDeleted方法

Java代码   收藏代码
  1. public void nodeDeleted(final String path) {  
  2.   if (path.startsWith(this.watcher.assignmentZNode)) {  
  3.     String regionName = ZKAssign.getRegionName(this.master.getZooKeeper(), path);  
  4.     RegionState rs = this.regionsInTransition.get(regionName);  
  5.     if (rs != null) {  
  6.       HRegionInfo regionInfo = rs.getRegion();  
  7.       if (rs.isSplit()) {  
  8.         LOG.debug("Ephemeral node deleted, regionserver crashed?, " +  
  9.           "clearing from RIT; rs=" + rs);  
  10.         regionOffline(rs.getRegion());  
  11.       } else {  
  12.         LOG.debug("The znode of region " + regionInfo.getRegionNameAsString()  
  13.             + " has been deleted.");  
  14.         if (rs.isOpened()) {  
  15.           makeRegionOnline(rs, regionInfo);  
  16.         }  
  17.       }  
  18.     }  
  19.   }  
  20. }  

 region上线,将region从transition列表中删除,并更新servers和regions列表

Java代码   收藏代码
  1. void regionOnline(HRegionInfo regionInfo, ServerName sn) {  
  2.   synchronized (this.regionsInTransition) {  
  3.     RegionState rs =  
  4.       this.regionsInTransition.remove(regionInfo.getEncodedName());  
  5.     if (rs != null) {  
  6.       this.regionsInTransition.notifyAll();  
  7.     }  
  8.   }  
  9.   synchronized (this.regions) {  
  10.     // Add check  
  11.     ServerName oldSn = this.regions.get(regionInfo);  
  12.     if (oldSn != null && serverManager.isServerOnline(oldSn)) {  
  13.       LOG.warn("Overwriting " + regionInfo.getEncodedName() + " on old:"  
  14.           + oldSn + " with new:" + sn);  
  15.       // remove region from old server  
  16.       Set<HRegionInfo> hris = servers.get(oldSn);  
  17.       if (hris != null) {  
  18.         hris.remove(regionInfo);  
  19.       }  
  20.     }  
  21.       
  22.     if (isServerOnline(sn)) {  
  23.       this.regions.put(regionInfo, sn);  
  24.       addToServers(sn, regionInfo);  
  25.       this.regions.notifyAll();  
  26.     } else {  
  27.       LOG.info("The server is not in online servers, ServerName=" +   
  28.         sn.getServerName() + ", region=" + regionInfo.getEncodedName());  
  29.     }  
  30.   }  
  31.   // Remove plan if one.  
  32.   clearRegionPlan(regionInfo);  
  33.   // Add the server to serversInUpdatingTimer  
  34.   addToServersInUpdatingTimer(sn);  
  35. }  

 

小节

region assignment主要关键点

1.region load balance,默认是随即均匀分配

2.master在/hbase/unassigned下建立region节点,方便后续和rs交互

3.rs初始化region在HDFS上的文件目录,包括.regioninfo文件和family目录

4.rs open region之后,将状态设为’opened‘,master认为region assignment成功,删除节点,并将region保存到online列表

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值