
# 如何配置HBase双读功能
#### 操作场景
HBase客户端应用通过自定义加载主备集群配置项，实现了双读能力。HBase双读作为提高HBase集群系统高可用性的一个关键特性，适用于使用**Get** 读取数据、使用批量**Get** 读取数据、使用**Scan** 读取数据，以及基于二级索引查询。它能够同时读取主备集群数据，减少查询毛刺，具体表现为：
- 高成功率：双并发读机制，保证每一次读请求的成功率。
- 可用性：单集群故障时，查询业务不中断。短暂的网络抖动也不会导致查询时间变长。
- 通用性：双读特性不支持双写，但不影响原有的实时写场景。
- 易用性：客户端封装处理，业务侧不感知。
![](https://support.huaweicloud.com/devg-lts-mrs/public_sys-resources/note_3.0-zh-cn.png)
- HBase双读特性基于Replication实现，备集群读取的数据可能和主集群存在差异，因此只能实现最终一致性。
- 目前HBase双读功能仅用于查询。主集群故障时，最新数据无法同步，备集群可能查询不到最新数据。
- HBase的**Scan**操作可能分解为多次RPC。由于相关session信息在不同集群间不同步，数据不能保证完全一致，因此双读只在第一次RPC时生效，ResultScanner close之前的请求会固定访问第一次RPC时使用的集群。
- HBase Admin接口、实时写入接口只会访问主集群。所以主集群故障后，不能提供Admin接口功能和实时写入接口功能，只能提供**Get** 、**Scan**查询服务。
 
HBase双读支持以下两种方式设置主备集群的相关配置：
- 在"hbase-dual.xml"中新增主备集群的相关配置。
- 将主备集群相关配置设置到HBaseMultiClusterConnection中（仅MRS 3.3.0及之后版本支持）。
#### 在"hbase-dual.xml"中新增主备集群配置
1. 参考[准备HBase应用开发和运行环境](https://support.huaweicloud.com/devg-lts-mrs/mrs_07_290006.html)章节，获取HBase主集群客户端配置文件**"**core-site.xml"、"hbase-site.xml"、"hdfs-site.xml"，并将其放置到"src/main/resources/conf/active"目录下，该目录需要自己创建。
2. 参考[准备HBase应用开发和运行环境](https://support.huaweicloud.com/devg-lts-mrs/mrs_07_290006.html)章节，获取备集群客户端配置文件**"** core-site.xml**"**、"hbase-site.xml"、"hdfs-site.xml"，并将其放置到"src/main/resources/conf/standby"目录下，该目录需要自己创建。
3. 创建"hbase-dual.xml"配置文件，放置到"src/main/resources/conf/"目录下。配置文件中的配置项可参考[HBase双读操作相关配置项说明]。
   
   ```
   <?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>
   ```
   
   
4. 创建双读Configuration。 
   - MRS 3.3.0之前版本，下面代码片段在"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);
       }
     ```
     
   
   - MRS 3.3.0及之后版本，取消"com.huawei.bigdata.hbase.examples"包的"TestMain"类**main** 方法中的**testHBaseDualReadSample**注释，确保"com.huawei.bigdata.hbase.examples"包的"HBaseDualReadSample"类中的"IS_CREATE_CONNECTION_BY_XML"值为"true"。
   
   
   
   
5. 确定数据来源的集群及集群角色。 
   - GET请求，以下代码片段在"com.huawei.bigdata.hbase.examples"包的"HBaseSample"类的**testGet** 方法中添加。
     ```
     Result result = table.get(get); 
     if (result instanceof DualResult) {
          LOG.info(((DualResult)result).getClusterId()); 
     }
     ```
     MRS 3.6.0-LTS及之后版本：
     ```
     Result result = table.get(get); 
     if (result instanceof DualResult) {
          LOG.info(((DualResult)result).getClusterId());
          LOG.info(((DualResult)result).getClusterRole());
     }
     ```
     
   
   - Scan请求，以下代码片段在"com.huawei.bigdata.hbase.examples"包的"HBaseSample"类的**testScanData** 方法中添加。
     ```
     ResultScanner rScanner = table.getScanner(scan);  
     if (rScanner instanceof HBaseMultiScanner) {
          LOG.info(((HBaseMultiScanner)rScanner).getClusterId()); 
     }
     ```
     MRS 3.6.0-LTS及之后版本：
     ```
     ResultScanner rScanner = table.getScanner(scan);  
     if (rScanner instanceof HBaseMultiScanner) {
          LOG.info(((HBaseMultiScanner)rScanner).getClusterId()); 
          LOG.info(((HBaseMultiScanner)rScanner).getClusterRole());
     }
     ```
     
   
   
   
   
6. 客户端支持打印metric信息 
   "log4j.properties"文件中增加如下内容，客户端将metric信息输出到指定文件。指标项信息可参考[打印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
   ```
   MRS 3.6.0-LTS及之后版本，如果使用"log4j2.properties" ，文件中增加如下内容，客户端将metric信息输出到指定文件：
   ```
   appender.DUAL.type = RollingFile
   appender.DUAL.name = DUAL
   appender.DUAL.fileName = /var/log/dual.log
   appender.DUAL.filePattern = /var/log/dual.log.%i
   appender.DUAL.layout.type = PatternLayout
   appender.DUAL.layout.pattern = %d{ISO8601} %-5p [%t] %c{2}: %.1000m%n
   appender.DUAL.policies.type = Policies
   appender.DUAL.policies.size.type = SizeBasedTriggeringPolicy
   appender.DUAL.policies.size.size = ${sys:hbase.security.log.maxfilesize:-256MB}
   appender.DUAL.strategy.type = DefaultRolloverStrategy
   appender.DUAL.strategy.max = ${sys:hbase.security.log.maxbackupindex:-20}
   logger.DUALLOGGER.name = org.apache.hadoop.hbase.client.dual
   logger.DUALLOGGER.additivity = false
   logger.DUALLOGGER = DEBUG,DUAL
   ```
   
   
 
#### 将主备集群相关配置设置到HBaseMultiClusterConnection中
该操作仅适用于MRS 3.3.0及之后版本。
1. 创建双读Configuration，取消"com.huawei.bigdata.hbase.examples"包的"TestMain"类**main** 方法中的**testHBaseDualReadSample**注释，确保"com.huawei.bigdata.hbase.examples"包的"HBaseDualReadSample"类中的"IS_CREATE_CONNECTION_BY_XML"值为"false"。
2. 在"HBaseDualReadSample"类的**addHbaseDualXmlParam** 方法中添加相关配置，相关配置项可参考[HBase双读操作相关配置项说明]。
   
   ```
   private void addHbaseDualXmlParam(Configuration conf) {
       // We need to set the optional parameters contained in hbase-dual.xml to conf
       // when we use configuration transfer solution
       conf.set(CONNECTION_IMPL_KEY, DUAL_READ_CONNECTION);
       // conf.set("", "");
   }
   ```
   
   
3. 在"HBaseDualReadSample"类的**initActiveConf** 方法中添加主集群客户端相关配置：
   
   ```
   private void initActiveConf() {
       // The hbase-dual.xml configuration scheme is used to generate the client configuration of the active cluster.
       // In actual application development, you need to generate the client configuration of the active cluster.
       String activeDir = HBaseDualReadSample.class.getClassLoader().getResource(Utils.CONF_DIRECTORY).getPath()
           + File.separator + ACTIVE_DIRECTORY + File.separator;
       Configuration activeConf = Utils.createConfByUserDir(activeDir);
       HBaseMultiClusterConnection.setActiveConf(activeConf);
   }
   ```
   
   
4. 在"HBaseDualReadSample"类initStandbyConf方法中添加备集群客户端相关配置： 
   ```
   private void initStandbyConf() {
       // The hbase-dual.xml configuration scheme is used to generate the client configuration of the standby cluster.
       // In actual application development, you need to generate the client configuration of the standby cluster.
       String standbyDir = HBaseDualReadSample.class.getClassLoader().getResource(Utils.CONF_DIRECTORY).getPath()
           + File.separator + STANDBY_DIRECTORY + File.separator;
       Configuration standbyConf = Utils.createConfByUserDir(standbyDir);
       HBaseMultiClusterConnection.setStandbyConf(standbyConf);
   }
   ```
   
   
5. 确定数据来源的集群及集群角色。 
   - GET请求，以下代码片段在"com.huawei.bigdata.hbase.examples"包的"HBaseSample"类的**testGet** 方法中添加。
     ```
     Result result = table.get(get); 
     if (result instanceof DualResult) {
          LOG.info(((DualResult)result).getClusterId()); 
     }
     ```
     MRS 3.6.0-LTS及之后版本：
     ```
     Result result = table.get(get); 
     if (result instanceof DualResult) {
          LOG.info(((DualResult)result).getClusterId()); 
          LOG.info(((DualResult)result).getClusterRole());
     }
     ```
     
   
   - Scan请求，以下代码片段在"com.huawei.bigdata.hbase.examples"包的"HBaseSample"类的**testScanData** 方法中添加。
     ```
     ResultScanner rScanner = table.getScanner(scan);  
     if (rScanner instanceof HBaseMultiScanner) {
          LOG.info(((HBaseMultiScanner)rScanner).getClusterId()); 
     }
     ```
     MRS 3.6.0-LTS及之后版本：
     
   
   
   
   
6. 客户端支持打印metric信息。 
   "log4j.properties"文件中增加如下内容，客户端将metric信息输出到指定文件。指标项信息可参考[打印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
   ```
   MRS 3.6.0-LTS及之后版本，如果使用"log4j2.properties" ，文件中增加如下内容，客户端将metric信息输出到指定文件：
   ```
   appender.DUAL.type = RollingFile
   appender.DUAL.name = DUAL
   appender.DUAL.fileName = /var/log/dual.log
   appender.DUAL.filePattern = /var/log/dual.log.%i
   appender.DUAL.layout.type = PatternLayout
   appender.DUAL.layout.pattern = %d{ISO8601} %-5p [%t] %c{2}: %.1000m%n
   appender.DUAL.policies.type = Policies
   appender.DUAL.policies.size.type = SizeBasedTriggeringPolicy
   appender.DUAL.policies.size.size = ${sys:hbase.security.log.maxfilesize:-256MB}
   appender.DUAL.strategy.type = DefaultRolloverStrategy
   appender.DUAL.strategy.max = ${sys:hbase.security.log.maxbackupindex:-20}
   logger.DUALLOGGER.name = org.apache.hadoop.hbase.client.dual
   logger.DUALLOGGER.additivity = false
   logger.DUALLOGGER = DEBUG,DUAL
   ```
   
   
 
 #### 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 |
   
