
# 如何配置HBase双读能力
#### 操作场景
HBase客户端应用通过自定义加载主备集群配置项，实现了双读能力。HBase双读作为提高HBase集群系统高可用性的一个关键特性，适用于四个查询场景：使用**Get** 读取数据、使用批量**Get** 读取数据、使用**Scan** 读取数据，以及基于二级索引查询。它能够同时读取主备集群数据，减少查询毛刺，具体表现为：
- 高成功率：双并发读机制，保证每一次读请求的成功率。
- 可用性：单集群故障时，查询业务不中断。短暂的网络抖动也不会导致查询时间变长。
- 通用性：双读特性不支持双写，但不影响原有的实时写场景。
- 易用性：客户端封装处理，业务侧不感知。
![](https://support.huaweicloud.com/devg3-mrs/public_sys-resources/note_3.0-zh-cn.png)
HBase双读使用约束：
- HBase双读特性基于Replication实现，备集群读取的数据可能和主集群存在差异，因此只能实现最终一致性。
- 目前HBase双读功能仅用于查询。主集群宕机时，最新数据无法同步，备集群可能查询不到最新数据。
- HBase的**Scan**操作可能分解为多次RPC。由于相关session信息在不同集群间不同步，数据不能保证完全一致，因此双读只在第一次RPC时生效，ResultScanner close之前的请求会固定访问第一次RPC时使用的集群。
- HBase Admin接口、实时写入接口只会访问主集群。所以主集群宕机后，不能提供Admin接口功能和实时写入接口功能，只能提供**Get** 、**Scan**查询服务。
 
#### 操作步骤
1. 参考[准备连接HBase集群配置文件](https://support.huaweicloud.com/devg3-mrs/mrs_07_290079.html)章节，获取HBase主集群客户端配置文件**"**core-site.xml"、"hbase-site.xml"、"hdfs-site.xml"，并将其放置到"src/main/resources/conf/active"目录下，该目录需要自己创建。
2. 参考[准备连接HBase集群配置文件](https://support.huaweicloud.com/devg3-mrs/mrs_07_290079.html)章节，获取备集群客户端配置文件**"** core-site.xml**"**、"hbase-site.xml"、"hdfs-site.xml"，并将其放置到"src/main/resources/conf/standby"目录下，该目录需要自己创建。
3. 创建"hbase-dual.xml"配置文件，放置到"src/main/resources/conf/"目录下。 
   ```
   <?xml version="1.0" encoding="UTF-8"?>
   <configuration>
   <!--主集群配置文件目录-->
       <property>
           <name>hbase.dualclient.active.cluster.configuration.path</name>
           <value>{样例代码目录}\\src\\main\\resources\\conf\\active</value>
           </property>
   <!--备集群配置文件目录-->
       <property>
           <name>hbase.dualclient.standby.cluster.configuration.path</name>
           <value>{样例代码目录}\\src\\main\\resources\\conf\\standby</value>
       </property>
   <!--双读模式的Connection实现-->
       <property>
           <name>hbase.client.connection.impl</name>
           <value>org.apache.hadoop.hbase.client.HBaseMultiClusterConnectionImpl</value>
       </property>
   <!--普通模式-->
       <property>
           <name>hbase.security.authentication</name>
           <value>Simple</value>
       </property>
   <!--普通模式-->
       <property>
           <name>hadoop.security.authentication</name>
           <value>Simple</value>
       </property>
   ```
   
   
 
#### 代码样例
- 创建双读Configuration，下面代码片段在"com.huawei.bigdata.hbase.examples"包的"TestMain"类的**init** 方法中添加。
  ```
  private static void init() throws IOException {
      // Default load from conf directory
      conf = HBaseConfiguration.create();
      //In Windows environment
      String userdir = TestMain.class.getClassLoader().getResource("conf").getPath() + File.separator;
      //In Linux environment
      //String userdir = System.getProperty("user.dir") + File.separator + "conf" + File.separator;
      conf.addResource(new Path(userdir + "hbase-dual.xml"), false);
    }
  ```
  
- 确定数据来源的集群
  - GET请求，以下代码片段在"com.huawei.bigdata.hbase.examples"包的"HBaseSample"类的**testGet** 方法中添加。
    ```
    Result result = table.get(get); 
    if (result instanceof DualResult) {
         LOG.info(((DualResult)result).getClusterId()); 
    }
    ```
    
  
  - Scan请求，以下代码片段在"com.huawei.bigdata.hbase.examples"包的"HBaseSample"类的**testScanData** 方法中添加。
    ```
    ResultScanner rScanner = table.getScanner(scan);  
    if (rScanner instanceof HBaseMultiScanner) {
         LOG.info(((HBaseMultiScanner)rScanner).getClusterId()); 
    }
    ```
    
   
- 客户端支持打印metric信息 "log4j.properties"文件中增加如下内容，客户端将metric信息输出到指定文件。
  ```
  log4j.logger.DUAL=debug,DUAL 
  log4j.appender.DUAL=org.apache.log4j.RollingFileAppender 
  log4j.appender.DUAL.File=/var/log/dual.log //客户端本地双读日志路径，根据实际路径修改，但目录要有写入权限
  log4j.additivity.DUAL=false 
  log4j.appender.DUAL.MaxFileSize=${hbase.log.maxfilesize} 
  log4j.appender.DUAL.MaxBackupIndex=${hbase.log.maxbackupindex} 
  log4j.appender.DUAL.layout=org.apache.log4j.PatternLayout 
  log4j.appender.DUAL.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c{2}: %m%n
  ```
  
 
#### HBase双读操作相关配置项说明
表1hbase-dual.xml配置项 
| 配置项名称                                               | 配置项详解            | 默认值                | 级别   |
|:---|:---|:---|:---|
| hbase.dualclient.active.cluster.configuration.path  | 主集群HBase客户端配置目录  | 无                  | 必选配置 |
| hbase.dualclient.standby.cluster.configuration.path | 备集群HBase客户端配置目录  | 无                  | 必选配置 |
| dual.client.schedule.update.table.delay.second      | 更新开启容灾表列表的周期时间   | 5                  | 可选配置 |
| hbase.dualclient.glitchtimeout.ms                   | 可以容忍主集群的最大毛刺时间   | 50                 | 可选配置 |
| hbase.dualclient.slow.query.timeout.ms              | 慢查询告警日志          | 180000             | 可选配置 |
| hbase.dualclient.active.cluster.id                  | 主集群id            | ACTIVE             | 可选配置 |
| hbase.dualclient.standby.cluster.id                 | 备集群id            | STANDBY            | 可选配置 |
| hbase.dualclient.active.executor.thread.max         | 请求主集群的线程池max大小   | 100                | 可选配置 |
| hbase.dualclient.active.executor.thread.core        | 请求主集群的线程池core大小  | 100                | 可选配置 |
| hbase.dualclient.active.executor.queue              | 请求主集群的线程池queue大小 | 256                | 可选配置 |
| hbase.dualclient.standby.executor.thread.max        | 请求备集群的线程池max大小   | 100                | 可选配置 |
| hbase.dualclient.standby.executor.thread.core       | 请求备集群的线程池core大小  | 100                | 可选配置 |
| hbase.dualclient.standby.executor.queue             | 请求备集群的线程池queue大小 | 256                | 可选配置 |
| hbase.dualclient.clear.executor.thread.max          | 清理资源线程池max大小     | 30                 | 可选配置 |
| hbase.dualclient.clear.executor.thread.core         | 清理资源线程池core大小    | 30                 | 可选配置 |
| hbase.dualclient.clear.executor.queue               | 清理资源线程池queue大小   | Integer. MAX_VALUE | 可选配置 |
| dual.client.metrics.enable                          | 客户端metric信息是否打印  | true               | 可选配置 |
| dual.client.schedule.metrics.second                 | 客户端metric信息打印周期  | 300                | 可选配置 |
| dual.client.asynchronous.enable                     | 是否异步请求主备集群       | false              | 可选配置 |
   
#### 打印metric信息
表2基本指标项 
| Metric名称              | 描述                | 日志级别  |
|:---|:---|:---|
| total_request_count   | 周期时间内查询总次数        | INFO  |
| active_success_count  | 周期时间内主集群查询成功次数    | INFO  |
| active_error_count    | 周期时间内主集群查询失败次数    | INFO  |
| active_timeout_count  | 周期时间内主集群查询超时次数    | INFO  |
| standby_success_count | 周期时间内备集群查询成功次数    | INFO  |
| standby_error_count   | 周期时间内备集群查询失败次数    | INFO  |
| Active Thread pool    | 周期打印请求主集群的执行线程池信息 | DEBUG |
| Standby Thread pool   | 周期打印请求备集群的执行线程池信息 | DEBUG |
| Clear Thread pool     | 周期打印释放资源的执行线程池信息  | DEBUG |
   
表3针对GET、BatchGET、SCAN请求，分别打印Histogram指标项 
| Metric名称                   | 描述            | 日志级别 |
|:---|:---|:---|
| averageLatency(ms)         | 平均时延          | INFO |
| minLatency(ms)             | 最小时延          | INFO |
| maxLatency(ms)             | 最大时延          | INFO |
| 95thPercentileLatency(ms)  | 95%请求的最大时延    | INFO |
| 99thPercentileLatency(ms)  | 99%请求的最大时延    | INFO |
| 99.9PercentileLatency(ms)  | 99.9%请求的最大时延  | INFO |
| 99.99PercentileLatency(ms) | 99.99%请求的最大时延 | INFO |
   
