HBase-客户端请求

客户端相关参数

参数 默认值 含义
hbase.htable.threads.max 2147483647  线程池中的线程数量
hbase.htable.threads.keepalivetime 60秒 keepalive时间 
hbase.client.pause 1秒 重试的休眠时间 
hbase.client.retries.number 10 重试次数 
hbase.client.rpc.maxattempts 1  
hbase.rpc.timeout 60秒  
hbase.client.prefetch.limit 10  
hbase.client.write.buffer 2097152  
hbase.client.scanner.caching 1 一次从服务端抓取的数量 
hbase.client.keyvalue.maxsize -1  
hbase.meta.scanner.caching 100  

 

在连接之前会创建如下信息

ZooKeeperWatcher

ClusterId获取/hbase/hbaseid

MasterAddressTracker获取/hbase/master

RootRegionTracker获取/hbase/root-region-server(也是-ROOT-表所在的机器)

RCP Engineorg.apache.hadoop.hbase.ipc.WritableRpcEngine

 

 

 

 

 

查询过程

HBase-客户端请求_第1张图片
 

 

org.apache.hadoop.hbase.client.HConnectionManager#locateRegion()函数主要逻辑如下

  1. //假设此时请求一个test表  
  2. locateRegion() {  
  3.     ensureZookeeperTrackers();  //创建zookeeper相关连接并获取相关数据  
  4.     if(当前表示root表) {  
  5.     //返回root表的连接  
  6.     ServerName servername = this.rootRegionTracker.waitRootRegionLocation(this.rpcTimeout);  
  7.     return new HRegionLocation(HRegionInfo.ROOT_REGIONINFO,  
  8.             servername.getHostname(), servername.getPort());  
  9.     }  
  10.     else if(当前表是meta表) {  
  11.     locateRegion("-ROOT-")  
  12.     }  
  13.     else {  
  14.     locateRegionInMeta(".META.")  
  15.     }  
  16. }  

 

调用的堆栈图如下

HBase-客户端请求_第2张图片
 堆栈执行过程

  1. 6.waitRootRegionLocation()  检查/hbase的znode节点是否存在  
  2.      |  
  3.      |  
  4. 5.locateRegion()  
  5.      |  
  6.      |  
  7. 4.locateRegionInMeta()    在查找META表之前需要先找到ROOT表  
  8.      |   
  9.      |  
  10. 3.locateRegion()   
  11.      |  
  12.      |  
  13. 2.locateRegionInMeta()    在查找test表之前首先需要找到META表  
  14.      |  
  15.      |  
  16. 1.locateRegion()  

 

 

 

 

 

ROOT表和META表内容

root表

row column value
.META.,,1 info:regioninfo

NAME => '.META.,,1', STARTKEY => '',

ENDKEY => '', ENCODED => 1028785192,

.META.,,1 info:server myRegionServerA:60020
.META.,,1 info:serverstartcode 1423222810704
.META.,,1 info:v \x00\x00

 

meta表

row column value

test,,1396452445291.79608271

b40352162a9f255817ce44bf.

info:regioninfo

NAME => 'test,,1396452445291.79608271b

40352162a9f255817ce44bf.', STARTKEY => '',

ENDKEY => 'bbb_mykey98',

ENCODED => 79608271b40352

162a9f255817ce44bf,

test,,1396452445291.79608271

b40352162a9f255817ce44bf.

info:server myRegionServerF:60020

test,,1396452445291.79608271

b40352162a9f255817ce44bf.

info:serverstartcode 1410425110025

test,bbb_mykey99,1396452445

291.00f93d1c191a66031ece7a

4ce0eac493.

info:regioninfo

NAME => 'kvdb,bbb_mykey99,13964524

45291.00f93d1c191a66031ece7a4ce0eac

493.', STARTKEY => 'bbb_mykey99', END

KEY => 'fff_mykey', ENCODED => 00f93d

1c191a66031ece7a4ce0eac493,

test,bbb_mykey99,1396452445

291.00f93d1c191a66031ece7a

4ce0eac493.

info:server myRegionServerC:60020

test,bbb_mykey99,1396452445

291.00f93d1c191a66031ece7a

4ce0eac493.

info:serverstartcode 1410425110025

zeroTable,,1423538052180.31

dd24d20f348098f3bf05313478177f. 

info:regioninfo

NAME => 'zeroTable,,142353805218

0.31dd24d20f348098f3bf0531347817

7f.', STARTKEY => '', ENDKEY => 'zz_99',

ENCODED => 31dd24d20f348098f3bf05313478177f,

zeroTable,,1423538052180.31

dd24d20f348098f3bf05313478177f. 

info:server myRegionServerB:60020

zeroTable,,1423538052180.31

dd24d20f348098f3bf05313478177f. 

info:serverstartcode 1423222810704

 

 

 

 

 

root表的定位和查询过程

  1. waitRootRegionLocation()  
  2. 1.这里首先检查/hbase这个根节点是否存在,如果不存在直接返回false  
  3. if (ZKUtil.checkExists(watcher, watcher.baseZNode) == -1) {  
  4.         return false;  
  5.       }  
  6.   
  7. 2.之后获取root表所在的机器信息(也就是/hbase/root-region-server节点信息)  
  8. return dataToServerName(super.blockUntilAvailable(timeout, true));  
  9.   
  10. 3.之后会触发一次RPC连接  
  11.           HRegionInterface server =  
  12.             getHRegionConnection(metaLocation.getHostname(), metaLocation.getPort());  
  13. 最终会创建一个代理类,也就是一个RPC类,连接到-ROOT-表所在的机器  
  14. 这个连接也会放到缓存中  
  15.   
  16. 4.之后会触发一次RPC请求,连接到ROOT表所在的机器,然后确定待查询的META信息在哪个机器上,这次会生成一个key为,可以看出这里的格式为 .META.,[表名],[第一个key这里是空着的],99999,99999  
  17. .META.,test,,99999999999999,99999999999999(也就是META表中关于test的第一条记录,两个9999都是固定加上的没有任何意义做)  
  18. 如果线上环境数据量不是特别大的话,META表就只有一个region,反映到ROOT中就只有一个key(一个key有四个keyvalue,所以会有四条记录)  
  19. 之后会返回这些跟META相关的数据  

 

 

 

 

 

META表和一次get查询的定位过程

HBase-客户端请求_第3张图片
执行的主要逻辑如下:

  1. 1.根据ROOT表查到的META相关信息,比如通过ROOT获取到的信息如下  
  2. keyvalues={.META.,,1/info:regioninfo/1373105037550/Put/vlen=34/ts=0, .META.,,1/info:server/1410417243783/Put/vlen=15/ts=0, .META.,,1/info:serverstartcode/1410417243783/Put/vlen=8/ts=0, .META.,,1/info:v/1373105037550/Put/vlen=2/ts=0}  
  3.   
  4. 2.然后解析这个数据获取机器相关信息,再创建一个RPC连接到META机器上  
  5.   
  6. 3.执行一个MetaScanner#metaScan()查询  
  7.   
  8. 4.将获取的META主机信息缓存起来并返还  
  9.   
  10. 5.执行get查询  
  11.   
  12. 6.触发一次PRC查询,查询的key为zzzz_mykey,此时会先查询META表,key被封装为  
  13. test,zzzz_mykey,99999999999999这样的格式  
  14.   
  15. 7.再执行一次MetaScanner#metaScan()查询  
  16.   
  17. 8.像regionserver机器发起一次PRC查询并返还get查询的结果  

 

 

MetaScanner#metaScan()逻辑

  1. 1.根据startRow(比如test,zzzz_mykey,99999999999999)获取一个结果集Result  
  2.   这里的zzzz_mykey可以为空,这就相当于查询第一个key所在的region,返回的Result中就包含了这个  
  3.   region的元信息(region的启动时间,ecode编码,机器名称等。当把key通过RPC发到服务端后,  
  4.   服务端自动做前缀定位查询这个时间是固定的(第一个key和最后一个key定位时间相同)  
  5. 2.解析结果并生成一个RegionInfo  
  6. 3.创建Scan(默认获取10条结果)  这里的startRow就是刚刚Result中返回的信息  
  7.     Scan scan = new Scan(startRow).addFamily("info")  
  8. 4.遍历结果并将获取到的结果(主机信息)缓存起来  

 如果只是get查询的话,最后会返回这个机器的信息,然后向这个机器发起一次RPC请求,并返回最终结果。

 

 

 

 

 

get的执行过程

HBase-客户端请求_第4张图片
 
 
在代码中并不是调用ServerCallable,而是实现它的匿名内部类

HTable#get()函数如下

  1. public Result get(final Get get) throws IOException {  
  2.   return new ServerCallable(connection, tableName, get.getRow(), operationTimeout) {  
  3.         public Result call() throws IOException {  
  4.           return server.get(location.getRegionInfo().getRegionName(), get);  
  5.         }  
  6.       }.withRetries();  
  7. }  
  

 

ServerCallable#withRetries()函数简化内容

这里有一个重试的逻辑(默认10次),最后回调call()函数

当出现异常的时候,可能是因为region下线,分割等原因会尝试再次访问meta表并清空缓存中的内容

  1. for (int tries = 0; tries < numRetries; tries++) {  
  2.         try {  
  3.           beforeCall();  
  4.           connect(tries != 0);  
  5.           return call();  
  6.         } catch (Throwable t) {  
  7.           shouldRetry(t);  
  8.           t = translateException(t);  
  9.           if (t instanceof SocketTimeoutException ||  
  10.               t instanceof ConnectException ||  
  11.               t instanceof RetriesExhaustedException) {  
  12.             // if thrown these exceptions, we clear all the cache entries that  
  13.             // map to that slow/dead server; otherwise, let cache miss and ask  
  14.             // .META. again to find the new location  
  15.             HRegionLocation hrl = location;  
  16.             if (hrl != null) {  
  17.               getConnection().clearCaches(hrl.getHostnamePort());  
  18.             }  
  19.           } else if (t instanceof NotServingRegionException && numRetries == 1) {  
  20.             // Purge cache entries for this specific region from META cache  
  21.             // since we don't call connect(true) when number of retries is 1.  
  22.             getConnection().deleteCachedRegionLocation(location);  
  23.           }  
  24.           RetriesExhaustedException.ThrowableWithExtraContext qt =  
  25.             new RetriesExhaustedException.ThrowableWithExtraContext(t,  
  26.               System.currentTimeMillis(), toString());  
  27.           exceptions.add(qt);  
  28.           if (tries == numRetries - 1) {  
  29.             throw new RetriesExhaustedException(tries, exceptions);  
  30.           }  
  31.         } finally {  
  32.           afterCall();  
  33.         }  
  34.         try {  
  35.           Thread.sleep(ConnectionUtils.getPauseTime(pause, tries));  
  36.         } catch (InterruptedException e) {  
  37.           Thread.currentThread().interrupt();  
  38.           throw new IOException("Giving up after tries=" + tries, e);  
  39.         }  
  40.       }  
  41.       return null;  
  42.     }  
  43.       
  44. }  
 

 

WritableRpcEngine#invoke()函数如下

  1.   public Object invoke(Object proxy, Method method, Object[] args)  
  2.       throws Throwable {  
  3.     final boolean logDebug = LOG.isDebugEnabled();  
  4.     long startTime = 0;  
  5.     if (logDebug) {  
  6.       startTime = System.currentTimeMillis();  
  7.     }  
  8.   
  9.     HbaseObjectWritable value = (HbaseObjectWritable)  
  10.     client.call(new Invocation(method, protocol, args), address,  
  11.     protocol, ticket, rpcTimeout);  
  12.   
  13.     return value.get();  
  14.   }  
  15. }  
 

 

最终会调用到HBaseClient,然后发送一个socket请求

HBaseClient#call()函数如下

  1. public Writable call(Writable param, InetSocketAddress addr,  
  2.                      Classextends VersionedProtocol> protocol,  
  3.                      User ticket, int rpcTimeout)  
  4. throws InterruptedException, IOException {  
  5.   Call call = new Call(param);  
  6.   Connection connection = getConnection(addr, protocol, ticket, rpcTimeout, call);  
  7.   connection.sendParam(call);                 // send the parameter  
  8.   boolean interrupted = false;  
  9.   //noinspection SynchronizationOnLocalVariableOrMethodParameter  
  10.   synchronized (call) {  
  11.     while (!call.done) {  
  12.       try {  
  13.         call.wait();                           // wait for the result  
  14.       } catch (InterruptedException ignored) {  
  15.         // save the fact that we were interrupted  
  16.         interrupted = true;  
  17.       }  
  18.     }  
  19.     if (interrupted) {  
  20.       // set the interrupt flag now that we are done waiting  
  21.       Thread.currentThread().interrupt();  
  22.     }  
  23.   
  24.     if (call.error != null) {  
  25.       if (call.error instanceof RemoteException) {  
  26.         call.error.fillInStackTrace();  
  27.         throw call.error;  
  28.       }  
  29.       // local exception  
  30.       throw wrapException(addr, call.error);  
  31.     }  
  32.     return call.value;  
  33.   }  
  34. }  
 

 

HBaseClient#sendParam()函数如下

同步块中的  out.write(data, 0, dataLength);

是调用 org.apache.hadoop.net.SocketOutputStream底层是用NIO实现的

这里的call是HbaseClient的内部类Call,其发送的对象是Writable实现类,也就是可序列化的

Call中发送的参数是org.apache.hadoop.hbase.ipc.Invocation

发送的参数中包含两个值,一个是id,一个是对象(比如这里就是Get对象)

  1. protected void sendParam(Call call) {  
  2.   if (shouldCloseConnection.get()) {  
  3.     return;  
  4.   }  
  5.   // For serializing the data to be written.  
  6.   final DataOutputBuffer d = new DataOutputBuffer();  
  7.   try {          
  8.     d.writeInt(0xdeadbeef); // placeholder for data length  
  9.     d.writeInt(call.id);  
  10.     call.param.write(d);  
  11.     byte[] data = d.getData();  
  12.     int dataLength = d.getLength();  
  13.     // fill in the placeholder  
  14.     Bytes.putInt(data, 0, dataLength - 4);  
  15.     //noinspection SynchronizeOnNonFinalField  
  16.     synchronized (this.out) { // FindBugs IS2_INCONSISTENT_SYNC  
  17.       out.write(data, 0, dataLength);  
  18.       out.flush();  
  19.     }  
  20.   } catch(IOException e) {  
  21.     markClosed(e);  
  22.   } finally {  
  23.     //the buffer is just an in-memory buffer, but it is still polite to  
  24.     // close early  
  25.     IOUtils.closeStream(d);  
  26.   }  
  27. }  

 

 

 

 

 

delete的执行过程

HTable#delete()内容如下:

注意如果是批量删除的话最终会异步执行,然后等待结果。如果只删除一条则同步执行

  1. public void delete(final Delete delete)  
  2.  throws IOException {  
  3.    new ServerCallable(connection, tableName, delete.getRow(), operationTimeout) {  
  4.          public Boolean call() throws IOException {  
  5.            server.delete(location.getRegionInfo().getRegionName(), delete);  
  6.            return null// FindBugs NP_BOOLEAN_RETURN_NULL  
  7.          }  
  8.        }.withRetries();  
  9.  }  
  10.   
  11.  public void delete(final List deletes)  
  12.  throws IOException {  
  13.    Object[] results = new Object[deletes.size()];  
  14.    try {  
  15.      connection.processBatch((List) deletes, tableName, pool, results);  
  16.    } catch (InterruptedException e) {  
  17.      throw new IOException(e);  
  18.    } finally {  
  19.      // mutate list so that it is empty for complete success, or contains only failed records  
  20.      // results are returned in the same order as the requests in list  
  21.      // walk the list backwards, so we can remove from list without impacting the indexes of earlier members  
  22.      for (int i = results.length - 1; i>=0; i--) {  
  23.        // if result is not null, it succeeded  
  24.        if (results[i] instanceof Result) {  
  25.          deletes.remove(i);  
  26.        }  
  27.      }  
  28.    }  
  29.  }  

总体来说跟get差不多,这里就不再详细介绍了

后面的时序图中省略了HBaseClient部分(这个类主要是发送最终的请求,所以可以忽略)

从get的执行过程可以看到,有一个重试逻辑(默认10次),主要是region切分的时候下线等原因找不到出现的重试,对于scan,delete,put等操作都会有重试逻辑的,后面就省略这部分介绍了。

 

 

 

 

 

put的执行过程

HBase-客户端请求_第5张图片

这里实际上是已经假设put执行过程中调用了flushCommits()了,也就是写入后直接提交,如果不提交或者当前的缓存未满则不会有异步提交那些过程

  1. 首先初始化HTable的过程跟上面介绍的查询过程是一样的  
  2. 这里有两个put函数  
  3.   public void put(final Put put) throws IOException {  
  4.     doPut(put);  
  5.     if (autoFlush) {  
  6.       flushCommits();  
  7.     }  
  8.   }  
  9.   
  10.   public void put(final List puts) throws IOException {  
  11.     for (Put put : puts) {  
  12.       doPut(put);  
  13.     }  
  14.     if (autoFlush) {  
  15.       flushCommits();  
  16.     }  
  17.   }  
  18.   
  19.   
  20. doPut()首先将数据放到缓存中,当缓存满了之后再插入  
  21.   private void doPut(Put put) throws IOException{  
  22.     validatePut(put);  
  23.     writeBuffer.add(put);  
  24.     currentWriteBufferSize += put.heapSize();  
  25.     if (currentWriteBufferSize > writeBufferSize) {  
  26.       flushCommits();  
  27.     }  
  28.   }  
  29.   
  30. 最终会调用 HConnectionImplementation#processBatchCallback()它的核心逻辑如下  
  31. 1.定位具体的Region,使用locateRegion()函数具体过程跟上面介绍的内容一样  
  32.   这里会有一个重试,默认10次每次间隔1秒  
  33. 2.将List或者Put放到线程池中  
  34.         for (Entry> e: actionsByServer.entrySet()) {  
  35.           futures.put(e.getKey(), pool.submit(createCallable(e.getKey(), e.getValue(), tableName)));  
  36.         }  
  37. 3.遍历结果  
  38.         for (Entry> responsePerServer : futures.entrySet()) {  
  39.         HRegionLocation loc = responsePerServer.getKey();  
  40.         Future future = responsePerServer.getValue();  
  41.                 MultiResponse resp = future.get();  
  42.     }  
  43. 4.处理异常信息及扫尾工作  
  44.   
  45. 线程池中的线程会调用HConnectionImplementation#createCallable()  
  46. 最终发送一个RPC请求,将List或者Put中的内容发送给服务端  
  47. 发送的RPC是一个封装的MultiAction,这个对象里面有包含了多个Action,每个Action就是对一个Put的  
  48. 封装  
  49. 最终会返回MultiResponse,这是对象里面包含了多个Pair(一个Pair就是对KeyValue的封装),如果是  
  50. Put操作则也会根据插入的数据返回相应的Pair,但是里面的KeyValue是空  

 

 

 

 

 

scan的执行过程

HBase-客户端请求_第6张图片
执行过程如下

  1. //假设客户端调用代码如下  
  2. HTable table = new HTable(cfg, "test");  
  3. ResultScanner rs = table.getScanner(scan);  
  4. for(Result r : rs) {  
  5.     List list = r.list();  
  6.     for(KeyValue kv : list) {  
  7.         System.out.println(new String(kv.getRow())+"---"+new String(kv.getValue()))  
  8.     }  
  9. }  
  10.   
  11. //table.getScanner()会先初始化scan,核心逻辑为  
  12. nextScanner() {  
  13.     1.获取startKey和endKey  
  14.     2.scan对象.set(startKey)  
  15.     3.new ScannerCallable(scan对象,抓取数量)  
  16.     4.ScannerCallable.withRetries() //发送RPC请求  
  17. }  
  18. //再由ClientScanner调用ScannerCallable#call,ScannerCallable#call()的主要逻辑  
  19. //在scan之前需要先获得一个scan的ID,然后每次scan的时候都需要带上这个id  
  20. //最终发送的RPC有两个参数,一个是scan的ID,第二个是抓取的数量  
  21. call() {  
  22.     if(scannerId!=-1L && closed) {  
  23.         close();  
  24.     }  
  25.     else if(scannerID==-1L && !closed) {  
  26.         scannerId = openScanner();  
  27.     }  
  28.     else {  
  29.         Result[] rss = WritableRpcEngine.next(scannerId,caching);         
  30.     }  
  31.     return rss;  
  32. }  
  33.   
  34. //当执行到for(Result r : rs)时候会调用到  
  35. AbstractClientScanner#hasNext() {  
  36.     Result next = ClientScanner.next();  
  37.     return Result;  
  38. }  
  39.   
  40. //ClientScanner#next()核心逻辑  
  41. //当设置了抓取数量为2时候,执行RPC后服务端会返回两个Result,然后将这两个Result放到cache中  
  42. //当cache中没有数据时会再次向服务端发送RPC请求抓取数据  
  43. next() {  
  44.     if(cache.size() == 0) {  
  45.         ScannerCallable.setCaching(需要抓取的数量);  
  46.         Result[] values = ScannerCallable.withRetries();  
  47.          for (Result rs : values) {  
  48.             cache.add(rs);  
  49.          }  
  50.     }  
  51.     if(cache.size() > 0) {  
  52.         return cache.poll();  
  53.     }  
  54. }  

  

scan中如果出现了跨region的处理过程

HBase-客户端请求_第7张图片
上图是scan跨region时的一个时序图,其中关键的处理部分是ClientScanner#next()函数

countdown就是一次需要抓取的数量,比如一共抓取了5个,如果此时抓取了3个之后region就到头了,于是返回

在遍历的时候countdown就减减然后变成了2

do-while的while中有一个nextScanner(),前面两个都是判断条件,当没有出现跨regin访问时countdown都会变成0的,所以最后的nextScanner()就不会被触发了,而此时countdown是不为0的于是执行nextScanner()了

可以看到在nextScanner()中又会调用ServerCallable#connect()函数,获取一个远端的连接。而这个过程就是前面介绍的test表-->meta表-->root表的过程。

 

  1. public Result next() {  
  2.         int countdown = this.caching;  
  3.     callable.setCaching(this.caching);  
  4.     do {  
  5.         // Server returns a null values if scanning is to stop.  Else,  
  6.         // returns an empty array if scanning is to go on and we've just  
  7.         // exhausted current region.  
  8.         values = callable.withRetries();  
  9.         if (values != null && values.length > 0) {  
  10.         for (Result rs : values) {  
  11.             cache.add(rs);  
  12.             for (KeyValue kv : rs.raw()) {  
  13.                 remainingResultSize -= kv.heapSize();  
  14.             }  
  15.             countdown--;  
  16.             this.lastResult = rs;  
  17.         }  
  18.      }  
  19.      // Values == null means server-side filter has determined we must STOP  
  20.     }while (remainingResultSize > 0 && countdown > 0 && nextScanner(countdown, values == null));    
  21. }  
 

 

 

 

 

flush的过程

注意是 HTable#flushCommits,不是admin的flush


flush的过程实际上跟put,delete(批量删除)等很类似,相当于是做一次批量提交到远端   

重点看一下processBatchCallback()函数

  1. void processBatchCallback() {  
  2.     for (int tries = 0; tries < numRetries && retry; ++tries) {    
  3.         //1.第一步将所有的操作加入到MultiAction中  
  4.         Map> actionsByServer =  
  5.         new HashMap>();  
  6.         for (int i = 0; i < workingList.size(); i++) {  
  7.             Row row = workingList.get(i);  
  8.             if (row != null) {  
  9.                 HRegionLocation loc = locateRegion(tableName, row.getRow());  
  10.                 byte[] regionName = loc.getRegionInfo().getRegionName();  
  11.                 MultiAction actions = actionsByServer.get(loc);  
  12.                 if (actions == null) {  
  13.                     actions = new MultiAction();  
  14.                     actionsByServer.put(loc, actions);  
  15.                 }     
  16.                 Action action = new Action(row, i);  
  17.                 lastServers[i] = loc;  
  18.                 actions.add(regionName, action);  
  19.             }  
  20.         }  
  21.       
  22.       
  23.         //2.提交到线程池中  
  24.         Map> futures =  
  25.         new HashMap>(actionsByServer.size());  
  26.         for (Entry> e: actionsByServer.entrySet()) {  
  27.             futures.put(e.getKey(), pool.submit(createCallable(e.getKey(), e.getValue(), tableName)));  
  28.         }  
  29.               
  30.         //3.获取结果  
  31.         for (Entry> responsePerServer  
  32.         : futures.entrySet()) {  
  33.             HRegionLocation loc = responsePerServer.getKey();  
  34.             Future future = responsePerServer.getValue();  
  35.             MultiResponse resp = future.get();            
  36.         }      
  37.     }  
  38.             //4.处理异常  
  39.   
  40. }  

 

 

 

 

 

admin操作-flush

将指定的region或者全表中的所有region都刷新,从而强制将memstore中的数据写到HFile中

最终会调用HBaseClient#call()触发一个RPC请求,发送一个 flushRegion 调用

如果当前的表包含了10个region,则会触发10次 flushRegion调用

之后服务端接收到这个flushRegion请求后,将当前region中的数据刷新到HFile中

  1. public void flush(final byte [] tableNameOrRegionName) {  
  2.     Pair regionServerPair  
  3.     = getRegion(tableNameOrRegionName, ct);  
  4.     if (regionServerPair != null) {  
  5.         flush(regionServerPair.getSecond(), regionServerPair.getFirst());  
  6.     }  
  7.     else {  
  8.         final String tableName = tableNameString(tableNameOrRegionName, ct);  
  9.         List> pairs =  
  10.         MetaReader.getTableRegionsAndLocations(ct,tableName);  
  11.         for (Pair pair: pairs) {  
  12.             if (pair.getFirst().isOffline()) continue;  
  13.             if (pair.getSecond() == nullcontinue;  
  14.             flush(pair.getSecond(), pair.getFirst());    
  15.         }  
  16.     }          
  17. }  
  18.   
  19. private void flush(final ServerName sn, final HRegionInfo hri) {  
  20.     HRegionInterface rs = this.connection.getHRegionConnection(  
  21.     sn.getHostname(), sn.getPort());  
  22.     rs.flushRegion(hri);  
  23. }  

 

 

 

 

 

参考

[HBase]Region location

HBase源码分析:HTable put过程

你可能感兴趣的:(分布式,HBase)