HBase-服务端处理请求的过程

时间:2022-02-13 08:25:48

Region的架构


HBase-服务端处理请求的过程
 

 

HRegionServer:

配置:

hbase.client.retries.number (默认10)  客户端的重试次数

hbase.regionserver.msginterval (默认3*1000) ???

hbase.regionserver.checksum.verify(默认false) 是否启用checksum

hbase.server.thread.wakefrequency(默认10*1000) 线程检查频率

hbase.regionserver.numregionstoreport(默认10) ???

hbase.regionserver.handler.count(默认10) handler处理线程个数

hbase.regionserver.metahandler.count(默认10) 处理meta和root的线程个数

hbase.rpc.verbose(默认false)

hbase.regionserver.nbreservationblocks(默认4)

hbase.regionserver.compactionChecker.majorCompactPriority(默认Integer.MAX_VALUE)

 

HRegionServer的主要操作:

包含的类有

HRegion集合

Leases(租借时间检查)

HMasterRegionInterface(管理hbase)

HServerLoad(hbase负载)

CompactSplitThread(用于合并处理)

MemStoreFlusher(用于刷新memstore)

HLog(WAL相关)

LogRoller(日志回滚)

ZooKeeperWatcher(zk监听)

SplitLogWorker(用于切分日志)

ExecutorService(用户启动open,close HRegion的线程池)

ReplicationSourceService和ReplicationSinkService(replication相关)

HealthCheckChore(健康检查)

 

一些监听类

MasterAddressTracker

CatalogTracker

ClusterStatusTracker

 

一些函数

postOpenDeployTasks() 此函数用于更新root表或meta表

各种CURD,scanner,increment操作

multi操作(对于delete和put)

对HRegion的flush,close,open(提交到线程池去做)

split,compact操作,这些最终由一个具体的HRegion去完成

 

启动的线程

hbase.regionserver.executor.openregion.threads 3

hbase.regionserver.executor.openroot.threads 1

hbase.regionserver.executor.openmeta.threads 1

hbase.regionserver.executor.closeregion.threads 3

hbase.regionserver.executor.closeroot.threads 1

hbase.regionserver.executor.closemeta.threads 1

hlog roller

cache flusher

compact

health check

lease

WEB UI

replication

rpc server

split worker

 

 

HRegion

配置:

 

 

HRegion的主要操作:

1.CURD和increment操作

2.doMiniBatchMutation操作(用于delete和put)

3.对region的open,delete,init,close,以及addRegionToMeta等操作

4.snapshot

5.bulkload

6.split

7.compact(major,minor)

8.lock

包含的内部类

WriteState(在flush,close,compact时会根据这个类加锁)

RegionScannerImpl(scan的region级别操作)

 

 

 

 

 

coprocessor的处理原理

  1. //HRegion的构造函数  
  2. coprocessorHost = new RegionCoprocessorHost(this, rsServices, conf);  
  3.   
  4. //RegionCoprocessorHost类中  将自定义的coprocessor类加载进来,并放到集合中  
  5. protected SortedSet<E> coprocessors = new SortedCopyOnWriteSet<E>(new EnvironmentPriorityComparator());  
  6. public RegionCoprocessorHost类中() {  
  7.     // load system default cp's from configuration.  
  8.     loadSystemCoprocessors(conf,"hbase.coprocessor.region.classes");  
  9.       
  10.     // load system default cp's for user tables from configuration.  
  11.     if (!HTableDescriptor.isMetaTable(region.getRegionInfo().getTableName())) {  
  12.         loadSystemCoprocessors(conf,"hbase.coprocessor.user.region.classes");         
  13.     }  
  14.       
  15.     // load Coprocessor From HDFS  
  16.     loadTableCoprocessors(conf);  
  17. }  
  18.   
  19. public void load相关函数() {  
  20.     //1.从当前线程上下文classloader中找到类并加载  
  21.     //2.放到coporcessors集合中  
  22. }  
  23.   
  24. //coprocessor的执行过程  
  25. //coprocessorHost.preFlush()时候会遍历执行所有集合中的处理器  
  26. HRegion#flush() {  
  27.     //1.coprocessorHost.preFlush();  
  28.     //2.flush  
  29.     //3.coprocessorHost.postFlush();  
  30. }  

 

 

 

 

 

服务端接收处理过程

HBase-服务端处理请求的过程

 

HBaseServer$Listener的run()函数和doAccept()函数简化如下  这是一个独立的listene线程

  1. while (running) {  
  2.     SelectionKey key = null;  
  3.     selector.select(); // FindBugs IS2_INCONSISTENT_SYNC  
  4.     Iterator<SelectionKey> iter = selector.selectedKeys().iterator();  
  5.     while (iter.hasNext()) {  
  6.         key = iter.next();  
  7.         iter.remove();  
  8.         if (key.isValid()) {  
  9.             if (key.isAcceptable())  
  10.                 doAccept(key);  
  11.             }  
  12.         }  
  13.     }  
  14. }  
  15.   
  16. void doAccept(SelectionKey key) {  
  17.     ServerSocketChannel server = (ServerSocketChannel) key.channel();  
  18.     currentReader = (currentReader + 1) % readers.length;  
  19.     Reader reader = readers[currentReader];  
  20.     readSelector.wakeup();  
  21.     SelectionKey readKey = reader.registerChannel(channel);  
  22.     c = getConnection(channel, System.currentTimeMillis());  
  23.     readKey.attach(c);  
  24. }  

 

HBaseServer$Listener$Reader的run()函数简化如下   这是一个独立的select线程

  1. while (running) {  
  2.     SelectionKey key = null;  
  3.     readSelector.select();  
  4.     while (adding) {  
  5.         this.wait(1000);  
  6.     }     
  7.     Iterator<SelectionKey> iter = readSelector.selectedKeys().iterator();  
  8.     while (iter.hasNext()) {  
  9.         key = iter.next();  
  10.         iter.remove();  
  11.         if (key.isValid()) {  
  12.             if (key.isReadable()) {  
  13.                 doRead(key);  
  14.             }  
  15.         }  
  16.     }  
  17. }  
  18.   
  19. //doRead()主要是读取远端的数据并解析处理  
  20. //没有这个process()函数,只是将逻辑简化了一下展示而言  
  21. //解析id,param并封装成一个Call对象,插入到并发队列中,之后由Handler线程处理  
  22. void process() {  
  23.     int id = dis.readInt();   
  24.     param = ReflectionUtils.newInstance(paramClass, conf);//read param  
  25.     param.readFields(dis);  
  26.     Call call = new Call(id, param, this, responder, callSize);  
  27.               
  28.     if (priorityCallQueue != null && getQosLevel(param) > highPriorityLevel) {  
  29.         priorityCallQueue.put(call);  
  30.     } else if (replicationQueue != null && getQosLevel(param) == HConstants.REPLICATION_QOS) {  
  31.         replicationQueue.put(call);  
  32.     } else {  
  33.         callQueue.put(call); // queue the call; maybe blocked here  
  34.     }  
  35. }  

 

HBaserServer$Handler的run()函数简化如下

  1. public void run() {  
  2.     //这里的myCallQueue和callQueue是一个队列  
  3.     Call call = myCallQueue.take();  
  4.     Invocation call = (Invocation)param;  
  5.     Method method = protocol.getMethod(call.getMethodName(),  
  6.         call.getParameterClasses());  
  7.         Object[] params = call.getParameters();  
  8.     Object value = method.invoke(impl, params);  
  9.       
  10.     //最后会调用到HBaserServer自身的某个函数  
  11.         //onlineRegions 是ConcurrentHashMap<String, HRegion>()  
  12.         String name = HRegionInfo.encodeRegionName(regionName)  
  13.         onlineRegions.get(name);  
  14.     Result r = region.getClosestRowBefore(row, family);  
  15.     return r;  
  16. }  

 

 

 

 

 

flush的过程

HBase-服务端处理请求的过程

服务端是收到了客户端发来的flushRegion请求,具体过程参见 客户端请求过程一文

客户端如果是flush全表,先是获取这个表的所有region名字,然后做一次批量的flushRegion请求(多个请求),但是所有的请求都是在一个线程中执行的

和flush相关的类函数简化如下,1-4是调用顺序

1.HRegion#flushcache()

2.HRegion#internalFlushcache()

3.Store#internalFlushCache()

4.StoreFile$Writer#append()

  1. //刷新region中的数据,注意有一个读锁  
  2. HRegion#flushcache() {  
  3.     try {  
  4.         lock.readLock().lock();  
  5.         internalFlushcache(status);  
  6.     } finally {  
  7.         lock.readLock().unlock();  
  8.     }  
  9. }  
  10.   
  11. //这里是遍历获取region中的所有store,然后对每个store都创建一个  
  12. //StoreFlusher对象,使用这个对象来刷新数据  
  13. //注意在获取所有Store的时候使用了写锁  
  14. HRegion#internalFlushcache() {  
  15.     try {  
  16.         this.updatesLock.writeLock().lock();  
  17.         List<StoreFlusher> storeFlushers = new ArrayList<StoreFlusher>(stores.size());  
  18.         for (Store s : stores.values()) {  
  19.             storeFlushers.add(s.getStoreFlusher(completeSequenceId));  
  20.         }     
  21.     } finally {  
  22.         this.updatesLock.writeLock().unlock();  
  23.     }  
  24.     for (StoreFlusher flusher : storeFlushers) {  
  25.         flusher.flushCache(status);  
  26.     }  
  27. }  
  28.   
  29. //将memstore中的数据取出然后遍历所有的KV  
  30. //将其刷新到HFile中,注意刷新的时候有一个flush锁  
  31. Store#internalFlushCache() {  
  32.     InternalScanner scanner = null;  
  33.     KeyValueScanner memstoreScanner = new CollectionBackedScanner(set, this.comparator);      
  34.       
  35.     Scan scan = new Scan();  
  36.     scan.setMaxVersions(scanInfo.getMaxVersions());  
  37.     scanner = new StoreScanner(this, scanInfo, scan,  
  38.         Collections.singletonList(memstoreScanner), ScanType.MINOR_COMPACT,  
  39.         this.region.getSmallestReadPoint(), HConstants.OLDEST_TIMESTAMP);  
  40.   
  41.     try {  
  42.         flushLock.lock();  
  43.         StoreFile.Writer writer = createWriterInTmp(set.size());  
  44.         List<KeyValue> kvs = new ArrayList<KeyValue>();  
  45.         boolean hasMore;  
  46.         do {  
  47.             hasMore = scanner.next(kvs, compactionKVMax);  
  48.             for (KeyValue kv : kvs) {  
  49.                 Writer.append(kv);  
  50.                 flushed += this.memstore.heapSizeChange(kv, true);        
  51.             }  
  52.             kvs.clear();  
  53.         }while(hasMore);      
  54.     } finally {  
  55.         flushLock.unlock();  
  56.     }  
  57. }  
  58.   
  59. //如果配置了布隆过滤器这里也会创建,最后调用  
  60. //HFileWriterV2将数据写入  
  61. StoreFile$Writer#append(final KeyValue kv) {  
  62.     appendGeneralBloomfilter(kv);  
  63.     appendDeleteFamilyBloomFilter(kv);  
  64.     HFileWriterV2#append(kv);  
  65.     trackTimestamps(kv);  
  66. }  

  

 

 

 

 

单个多个put和多个delete的过程

HBase-服务端处理请求的过程

最终是将KeyValue存到KeyValueSkipListSet中,这个类内部是采用ConcurrentSkipListMap实现的

服务端是接收到客户端发来的multi请求

注意只有put操作(单个put和批量put操作)以及批量的delete操作才会执行上面的调用逻辑

incr和单个delete采用了不同的处理逻辑

简化的核心处理函数如下:

  1. //对put和delete操作,都会进到这个函数里面  
  2. HRegion#doMiniBatchMutation() {  
  3.     //1.试着获取锁  
  4.     //2.更新时间戳  
  5.     lock(this.updatesLock.readLock(), numReadyToWrite);  
  6.       
  7.     //3.写入到memstore中      
  8.     long addedSize = 0;  
  9.     for (int i = firstIndex; i < lastIndexExclusive; i++) {  
  10.         addedSize += applyFamilyMapToMemstore(familyMaps[i], w);      
  11.     }  
  12.       
  13.     //4.写入到WALEdit中  
  14.     addFamilyMapToWALEdit(familyMaps[i], walEdit);  
  15.       
  16.     //5.写入到HLog中(不做sync)  
  17.     HLog.appendNoSync(regionInfo, this.htableDescriptor.getName(),  
  18.         walEdit, first.getClusterId(), now, this.htableDescriptor);  
  19.           
  20.     //6.释放锁  
  21.     this.updatesLock.readLock().unlock();  
  22.     //7.同步WALEdit  
  23.     //8.mvcc相关  
  24.     mvcc.completeMemstoreInsert(w);   
  25.     //9.执行coprocessor hook  
  26. }  

这里没有memstore满了判断逻辑,而是由单独的一个线程(cacheFlusher)出处理的

写入到memstore的判断逻辑图

HBase-服务端处理请求的过程
 

 

 

 

 

incr的过程

核心处理逻辑如下

  1. HRegion#increment() {  
  2.     Map<Store, List<KeyValue>> tempMemstore = new HashMap<Store, List<KeyValue>>();  
  3.     try {  
  4.         Integer lid = getLock(lockid, row, true);  
  5.         lock(this.updatesLock.readLock());        
  6.         byte [] row = increment.getRow();  
  7.         Get get = new Get(row);  
  8.         List<KeyValue> results = get(get, false);  
  9.         for(KeyValue kv : results) {  
  10.             KeyValue kv = results.get();  
  11.             if(kv.getValueLength() == Bytes.SIZEOF_LONG) {  
  12.                 amount += Bytes.toLong(kv.getBuffer(), kv.getValueOffset(), Bytes.SIZEOF_LONG);  
  13.             } else {  
  14.                 throw new DoNotRetryIOException("Attempted to increment field that isn't 64 bits wide");  
  15.             }  
  16.         }  
  17.       
  18.         if (writeToWAL) {  
  19.             walEdits.add(newKV);  
  20.         }  
  21.         tempMemstore.put(store, kvs);  
  22.         //将WALEdit sync到HLog中  
  23.       
  24.         size = this.addAndGetGlobalMemstoreSize(size);  
  25.         flush = isFlushSize(size);  
  26.         if (flush) {  
  27.             requestFlush();  
  28.         }  
  29.     } finally {      
  30.         this.updatesLock.readLock().unlock();  
  31.         releaseRowLock(lid);  
  32.     }  
  33. }  

可以看到incrment的执行流程是先根据row创建Get对象,然后获取这个值,再对这个值做++操作

并将结果放到临时缓存中,如果缓存已满就做刷新

从获取数据到,再做++操作,最后写入缓存(可能还要做刷新处理)这么一段过程都是需要加锁处理的,加锁只是一个行锁

 

 

 

 

 

单个delete的过程

主要处理简化逻辑如下

  1. HRegion#delete(){  
  2.     try {  
  3.         lid = getLock(lockid, row, true);  
  4.         internalDelete()  
  5.     } finally {  
  6.         releaseRowLock(lid);  
  7.     }  
  8. }  
  9.   
  10. HRegion#internalDelete() {  
  11.     try {  
  12.         updatesLock.readLock().lock();  
  13.         //将KeyValue写入到WALEdit中  
  14.         for(family : 获取delete关联的所有famliy) {  
  15.             Store store = getStore(family);  
  16.             for (KeyValue kv: edits) {  
  17.                 kv.setMemstoreTS(localizedWriteEntry.getWriteNumber());  
  18.                 addedSize += store.add(kv);  
  19.             }  
  20.         }  
  21.         flush = isFlushSize(this.addAndGetGlobalMemstoreSize(addedSize));  
  22.         if (flush) {  
  23.             requestFlush();  
  24.         }  
  25.     } finally {  
  26.         updatesLock.readLock().unlock();  
  27.     }     
  28. }  

delete是将所有的column famliy都遍历一遍然后删除和这个key相关的所有famliy,并写入缓存中,如果缓存满了就做刷新处理,同时在删除的时候会有更新锁。

 

 

 

 

 

get的过程

下面是核心处理逻辑,可以看到get最后是通过scan来处理的,也就是简单的将scan包装了一下

  1. HRegion#get() {  
  2.     List<KeyValue> results = new ArrayList<KeyValue>();  
  3.     Scan scan = new Scan(get);  
  4.     RegionScanner scanner = getScanner(scan);  
  5.     List<KeyValue> list = scanner.next(results, SchemaMetrics.METRIC_GETSIZE);  
  6.     return Result(list);  
  7. }  

 

 

 

 

 

scan过程

scan是最复杂的操作,其中包含了getClosestRowBefore,openScanner,next三个操作

第一个是对用于对META和ROOT表操作的,第二个用于创建一个scan对象,第三个用于做遍历操作

首先看第一个closestRowBefore的时序图

HBase-服务端处理请求的过程
这里简单来说有这么几步操作

1.通过Store调用HFileReaderV2,这里主要用于打开一个HFile文件,然后定位到指定的key前面或者后面。 

   这步操作是用于在ROOT表中获取特定的KeyValue,info:server这个KeyValue,然后将这个值封装成

   Get对象再去查询META表

2.调用get函数对数据进行获取,get内部又是调用scan函数的,所以实际会创建一个StroeScanner对象

3.StoreScanner也就是对底层的HFileScanner的简单封装

4.之后调用next()获取一段数据,这里还会有嵌入了filter的执行逻辑

5.最后返回给用户的是Result结果,这里就是META表中的一条记录

 

getClosestRowBefore的调用栈如下

HBase-服务端处理请求的过程
 

scan操作的类图如下

HBase-服务端处理请求的过程
Store是核心的类,这个类中包含了若干个StoreFile,每个StoreFile类中又有一个Reader和Writer内部类。

通过Reader内部类可以返回一个StroeFileScanner对象

而最终上层在做scan的时候,是通过RegionScannerImpl去做的,这里就包含了filter的过滤逻辑。

执行逻辑如下

  1. //定位到一个具体的Store后,然后在这个Sotre中查找最接近指定key的KeyValue  
  2. //再根据这个KeyValue做一次get查询  
  3. //简单来说就是根据特定的key直接从HFile中查找最接近的KeyValue  
  4. //然后封装成Get操作,从META表中查询出List<KeyValue>并返回  
  5. HRegion#getClosestRowBefore() {  
  6.     startRegionOperation();  
  7.     Store store = getStore(family);  
  8.     KeyValue key = store.getRowKeyAtOrBefore(row);  
  9.     if (key != null) {  
  10.         Get get = new Get(key.getRow());  
  11.         get.addFamily(family);  
  12.         result = get(get, null);  
  13.     }  
  14. }  
  15.   
  16.   
  17. //先从memstore中查找最匹配的key,然后再遍历当前Store下的所有的HFile  
  18. //找到最匹配的那个key  
  19. //比如客户端发起查询.META.,test,,99999999999999,99999999999999  
  20. //实际找到key为(返回info:server那个KeyValue)  
  21. //.META.,,1/info:server/1423222815731/Put/vlen=23/ts=0  
  22. Store#getRowKeyAtOrBefore() {  
  23.     this.memstore.getRowKeyAtOrBefore(state);  
  24.     for (StoreFile sf : Lists.reverse(storefiles)) {  
  25.         rowAtOrBeforeFromStoreFile(sf, state);  
  26.     }  
  27. }  
  28.   
  29. //这里是定位到-ROOT-表中的info:server 这一个KeyValue并返回  
  30. Store#rowAtOrBeforeFromStoreFile() {  
  31.      HFileScanner scanner = r.getScanner(truetruefalse);  
  32.      if (!seekToScanner(scanner, firstOnRow, firstKV)) return;  
  33.      if (walkForwardInSingleRow(scanner, firstOnRow, state)) return;  
  34.      while (scanner.seekBefore(firstOnRow.getBuffer(), firstOnRow.getKeyOffset(),firstOnRow.getKeyLength())) {  
  35.         KeyValue kv = scanner.getKeyValue();  
  36.         if (!state.isTargetTable(kv)) break;  
  37.         if (!state.isBetterCandidate(kv)) break;  
  38.         // Make new first on row.  
  39.         firstOnRow = new KeyValue(kv.getRow(), HConstants.LATEST_TIMESTAMP);  
  40.         // Seek scanner.  If can't seek it, break.  
  41.         if (!seekToScanner(scanner, firstOnRow, firstKV)) break;  
  42.         // If we find something, break;  
  43.         if (walkForwardInSingleRow(scanner, firstOnRow, state)) break;  
  44.     }  
  45. }  
  46.   
  47.   
  48. //先是在缓存中查找,如果找到就返回  
  49. //否则就在HFile中查找,找到后再放到缓存中  
  50. //这里读取的是一个data block  
  51. HFileReaderV2#readBlock() {  
  52.     BlockCacheKey cacheKey = new BlockCacheKey(name, dataBlockOffset,  
  53.     dataBlockEncoder.getEffectiveEncodingInCache(isCompaction),  
  54.     expectedBlockType);  
  55.               
  56.     HFileBlock cachedBlock = (HFileBlock)cacheConf.getBlockCache().  
  57.     getBlock(cacheKey, cacheBlock, useLock);  
  58.     if (cachedBlock != null) {  
  59.         return cachedBlock;  
  60.     }                        
  61.     HFileBlock hfileBlock = fsBlockReader.readBlockData(dataBlockOffset,onDiskBlockSize, -1, pread);  
  62.     cacheConf.getBlockCache().cacheBlock(cacheKey, hfileBlock,cacheConf.isInMemory());                  
  63. }  
  64.   
  65.   
  66. //执行到这里的时候已经获取到key在META表中的接近key了  
  67. //然后在执行get操作根据META表的key再从META表中获取一条数据返回  
  68. //nextRaw最后会调用nextInternal做处理  
  69. HRegion$RegionScannerImpl#nextRaw() {  
  70. if (outResults.isEmpty()) {  
  71.         // Usually outResults is empty. This is true when next is called  
  72.         // to handle scan or get operation.  
  73.         returnResult = nextInternal(outResults, limit, metric);  
  74.       } else {  
  75.         List<KeyValue> tmpList = new ArrayList<KeyValue>();  
  76.         returnResult = nextInternal(tmpList, limit, metric);  
  77.         outResults.addAll(tmpList);  
  78.       }       
  79. }  
  80.   
  81.   
  82. //这个函数通过KeyValueHeap获取一条KeyValue  
  83. //KeyValueHeap是调用StoreScanner#next()  
  84. //而StoreScanner最终会调用HFileReaderv2$ScannerV2#next()  
  85. //获取一条KeyValue,最后返回一个List<KeyValue>,也就是Result  
  86. //返回结果为  
  87. //[.META.,,1/info:regioninfo/1423222781931/Put/vlen=34/ts=0,   
  88. //.META.,,1/info:server/1423222815731/Put/vlen=23/ts=0,   
  89. //.META.,,1/info:serverstartcode/1423222815731/Put/vlen=8/ts=0,   
  90. //.META.,,1/info:v/1423222781931/Put/vlen=2/ts=0]  
  91. HRegion$RegionScannerImpl#nextInternal() {  
  92.     // Let's see what we have in the storeHeap.  
  93.     KeyValue current = this.storeHeap.peek();     
  94.     //之后再做一些filter操作,判断是否需要终止后续逻辑  
  95. }  

 

openscanner的执行过程 

HBase-服务端处理请求的过程
执行逻辑如下 

  1. //这里的逻辑是创建一个RegionScanner对象,这个对象内部是封装了RegionScannerImpl  
  2. //最终是调用HFileReaderV2定位到一个具体的data block附近,然后将这个scann对象缓存起来  
  3. //并创建一个scannID,将id和scan对象放到map中,并将scannID返回给用户  
  4. //之后用户就根据这个scanID去做scan操作  
  5. HRegionServer#openScanner() {  
  6.     HRegion r = getRegion(regionName);  
  7.     RegionScanner s = r.getScanner(scan);     
  8.     return addScanner(s);  
  9. }  
  10.   
  11.   
  12. //创建RegionScannerImpl待以后使用  
  13. HRegion#instantiateRegionScanner() {  
  14.     //返回类型为RegionScanner  
  15.     return new RegionScannerImpl(scan, additionalScanners, this);     
  16. }  
  17.   
  18.   
  19. //RegionScannerImpl的构造函数  
  20. //此时会创建一个StoreScanner对象  
  21. //并调用StoreFileScanner#seek()  
  22. RegionScannerImpl#init() {  
  23.      for (Map.Entry<byte[], NavigableSet<byte[]>> entry :scan.getFamilyMap().entrySet()) {  
  24.         Store store = stores.get(entry.getKey());  
  25.         //这里会创建一个StoreScanner对象  
  26.         KeyValueScanner scanner = store.getScanner(scan, entry.getValue());   
  27.         scanners.add(scanner);     
  28.     }  
  29. }  
  30.   
  31.   
  32. StoreFileScanner#seek() {  
  33.     //1.定位到指定的key附近  
  34.     seekAtOrAfter()  
  35. }  
  36.   
  37.   
  38. //生成一个scannID,放到map中(map的key是scannID,value是RegionScannerImpl)  
  39. //最后再创建一个租借时间的监听器  
  40. HRegionServer#addScanner() {  
  41.     scannerId = rand.nextLong();  
  42.     String scannerName = String.valueOf(scannerId);  
  43.     scanners.put(scannerName, s);  
  44.     this.leases.createLease(scannerName, new ScannerListener(scannerName));   
  45. }  

 

next的执行过程

HBase-服务端处理请求的过程

执行逻辑如下

  1. //首先根据scannID获取scan对象  
  2. //然后使用这个scan对象获取数据  
  3. //最后返回Result[] 数组给客户端  
  4. HRegionServer#next() {  
  5.     RegionScanner s = this.scanners.get(scannID);  
  6.     this.leases.cancelLease(scannID);  
  7.     HRegion region = getRegion(s.getRegionInfo().getRegionName());    
  8.     List<Result> results = new ArrayList<Result>(nbRows);  
  9.     boolean moreRows = s.nextRaw(values, SchemaMetrics.METRIC_NEXTSIZE);  
  10.     results.add(new Result(values));  
  11.     this.leases.addLease(lease);  
  12.     //最终返回Result[] 数组     
  13. }  
  14.   
  15.   
  16. //使用RegionScannerImpl这个内部类来抓取数据  
  17. HRegion$RegionScannerImpl#nextRaw() {  
  18.     if (outResults.isEmpty()) {  
  19.         // Usually outResults is empty. This is true when next is called  
  20.         // to handle scan or get operation.  
  21.         returnResult = nextInternal(outResults, limit, metric);  
  22.     } else {  
  23.         List<KeyValue> tmpList = new ArrayList<KeyValue>();  
  24.         returnResult = nextInternal(tmpList, limit, metric);  
  25.         outResults.addAll(tmpList);  
  26.     }  
  27. }  
  28.   
  29.   
  30. //populateResult函数中调用KeyValueHeap#next()获取一条KeyValue  
  31. HRegion$RegionScannerImpl#nextInternal() {  
  32.     boolean stopRow = isStopRow(currentRow, offset, length);      
  33.     KeyValue nextKv = populateResult(results, this.storeHeap, limit, currentRow, offset,  
  34.     length, metric);  
  35.     //一系列的filter,过滤一些东西,看是否需要结束  
  36. }  
  37.   
  38.   
  39. //批量抓取一些KeyValue  
  40. KeyValueHeap#next() {  
  41.     InternalScanner currentAsInternal = (InternalScanner)this.current;  
  42.     boolean mayContainMoreRows = currentAsInternal.next(result, limit, metric);  
  43.     KeyValue pee = this.current.peek();  
  44.       
  45. }  
  46.   
  47.   
  48. //这里有很复杂的switch判断,主要给filter使用的  
  49. //根据不同的情况可能会出现重现定位reseek()  
  50. StoreScanner#next() {  
  51.     switch(code) {  
  52.         case SEEK_NEXT_ROW: {  
  53.             reseek(matcher.getKeyForNextRow(kv)); break;  
  54.         }  
  55.         case SEEK_NEXT_COL: {  
  56.             reseek(matcher.getKeyForNextColumn(kv)); break;   
  57.         }  
  58.         case SKIP: {  
  59.             KeyValueHeap.next();      
  60.         }  
  61.         //......  
  62.     }     
  63. }  
  64.   
  65.   
  66. //调用HFileReaderV2定位具体的data block  
  67. StoreFileScanner#reseek() {  
  68.     if (!reseekAtOrAfter(hfs, key)) {  
  69.         close();  
  70.         return false;  
  71.     }  
  72.     cur = HFileReaderV2$ScannerV2.getKeyValue();  
  73. }