深入 HBase 09 - Client 路由与重试
前置问题与边界
本篇回答一个核心问题:Client 如何缓存 hbase:meta 路由,Region 移动后怎样重新定位。
HBase Client 不是把请求发给 Master 再由 Master 转发。普通 Get、Put 和 Scan 会通过客户端定位找到 RegionServer,然后直接访问对应 Region。hbase:meta 是路由目录,ZooKeeper 或 registry 是引导与协调入口,HDFS 是底层文件系统;三者不能混成一个“元数据中心”。
HBase 2.6.6 中,Connection 是重对象,承载 RPC、线程、缓存和定位相关状态;Table、Admin、RegionLocator 是从 Connection 派生出来的轻量使用入口,使用后应该关闭。频繁创建 Connection 会增加资源和定位成本。
路由路径图
sequenceDiagram
participant App as Application
participant Conn as HBase Connection
participant Meta as hbase:meta
participant RS as RegionServer
App->>Conn: Get(row)
Conn->>Conn: check MetaCache
alt cache hit
Conn->>RS: RPC to cached region location
else cache miss
Conn->>Meta: locate region by row key
Meta-->>Conn: region location
Conn->>RS: RPC to RegionServer
end
alt moved or not serving
RS-->>Conn: region error
Conn->>Conn: reload or clear cache
Conn->>Meta: locate again
Conn->>RS: retry
end
RS-->>App: Result
正常路径依赖 MetaCache,异常路径刷新定位。把 HBase Client 写成“每次请求都查 hbase:meta”会夸大 meta 压力;把它写成“缓存永远可靠”又会忽略 Region 移动、split、merge 和失败恢复。
关键对象和状态
| 对象 | 生命周期建议 | 职责 |
|---|---|---|
Connection |
进程级或服务级复用 | 管理 RPC、线程、配置、定位缓存 |
Table |
短生命周期,使用后关闭 | 面向单表读写 |
Admin |
短生命周期,使用后关闭 | 表、namespace、Region 操作 |
RegionLocator |
短生命周期,使用后关闭 | 查询 Region 位置信息 |
| MetaCache | Connection 内部状态 |
缓存 table+row 到 Region location |
hbase:meta |
系统表 | 持久保存 Region 路由信息 |
| RegionServer | 数据面服务 | 接收对应 Region 的请求 |
Connection 复用是客户端工程实践的第一条边界。Web 服务中为每个 HTTP 请求创建一个 HBase Connection,会把连接初始化、线程、RPC 资源和 meta 定位都放大到请求粒度。更合理的做法是在应用生命周期内复用 Connection,每次操作按需获取并关闭 Table。
定位与缓存
客户端根据 table name 和 row key 定位 Region。第一次定位可能需要访问 hbase:meta,得到 start key、end key 和 RegionServer 地址后写入 MetaCache。后续落在同一个 range 的请求可以直接使用缓存。
HBase 2.6.6 还在 Public API 中提供了 RegionLocator#getRegionLocationsPage,用于分页获取 Region location。这说明 Region 定位不是只能一次性拉完整表,也不是每个 row 都查一次 meta;客户端 API 明确暴露了按页处理大量 Region location 的能力。
缓存失效与重试
Region split、merge、move、RegionServer 崩溃和 balancer 都可能让旧缓存失效。旧 RegionServer 收到请求后可能返回 not serving region、region moved 或连接类错误。客户端捕获这类错误后,会清理或刷新对应缓存,再重新定位并重试。
重试不是无限保证成功。hbase.client.retries.number、operation timeout、RPC timeout、pause 策略和调用语义共同决定客户端能等多久。应用层还要处理幂等性。Get 可以安全重试;Increment、Append、带条件的 mutation 需要结合服务端语义和返回状态判断,不能把所有失败都写成“客户端再发一次就没问题”。
Scan 的路由连续性
Scan 会从 start row 所在 Region 开始,拿到一段结果后继续跨 Region 前进。每跨到下一个 Region,客户端都需要新的 Region location。Region 在 scan 过程中移动时,scanner 可能失效,客户端需要重新打开 scanner 或按语义重试。
分页、cache、batch、limit 和 reversed scan 会在第 14 篇展开。本篇只保留路由事实:scan 是按 row-key range 跨 Region 推进,不是一次 RPC 把全表固定住。
最小实验
UNVERIFIED_RUNTIME:当前任务环境没有目标版本运行条件。以下步骤用于 HBase 2.6.6、Hadoop 3.4.3、JDK 17 环境,不包含伪造输出。
创建带预分区的表:
1 | |
1 | |
1 | |
写入两个 range 的 row key:
1 | |
1 | |
查看 Region 列表:
1 | |
移动某个 Region 到另一台 RegionServer。单节点环境不适合该实验;多节点测试环境需要把 Region 名和目标 server 替换成实际值:
1 | |
再次读取同一 row key:
1 | |
这组步骤只用于观察客户端重定位和 RegionServer 变化,不记录固定耗时或重试次数。不同配置、网络和 Region 状态会让表现不同。
清理实验表:
1 | |
1 | |
1 | |
Java Public API 示例
下面示例展示 Connection 复用、Table 短生命周期和 RegionLocator 查询。它不访问内部 MetaCache 类,也不直接读写 hbase:meta:
1 | |
UNVERIFIED_RUNTIME:本轮未在目标依赖下编译运行。HBase rel/2.6.6 的 RegionLocator Public API 中,getRegionLocationsPage 的签名是 getRegionLocationsPage(byte[] startKey, int limit);该方法是 optional default method,不能支持分页定位的实现可以抛出 UnsupportedOperationException。
失败恢复路径
当 RegionServer 宕机时,客户端先看到连接错误、超时或 Region 不可服务。Master 侧的 ServerCrashProcedure 和 assignment 会让其他 RegionServer 接管 Region,并处理 WAL 恢复。客户端侧只负责按重试策略刷新路由并重新发送请求。
写请求的失败边界比读请求更复杂。客户端超时不一定说明服务端没有执行,也不一定说明已经执行。应用如果在超时后盲目重试非幂等操作,可能产生重复影响。HBase 的行级原子性和 CheckAndMutate 边界会在第 10 篇展开。
工程迁移
| HBase 机制 | 通用模式 | 迁移场景 |
|---|---|---|
| MetaCache | 客户端路由缓存 | RPC service discovery、partition map cache |
| cache reload | 错误驱动失效 | DNS 缓存刷新、Kafka metadata refresh |
Connection 复用 |
重对象生命周期外提 | DB connection pool、HTTP client 复用 |
| RegionLocator paging | 大路由表分页 | shard map 分页、服务目录分页 |
模式公式是:request(key) = locate(key) -> cache(range) -> call(server) -> invalidate_on_error。听到“扩容后客户端仍访问旧节点”,先查缓存失效条件、错误分类和重试超时,而不是直接认为服务端路由没更新。
常见误解
| 误解 | 更准确的说法 |
|---|---|
| 所有请求都经 HMaster 转发 | 普通读写定位后直连 RegionServer |
每次 Get 都查询 hbase:meta |
客户端复用 MetaCache,缓存失效时才刷新 |
| ZooKeeper 保存所有 Region 路由 | hbase:meta 保存 Region 路由信息,ZooKeeper/registry 负责入口和协调状态 |
Connection 可以随请求创建 |
Connection 是重对象,应在应用生命周期内复用 |
| 重试可以掩盖所有失败 | 超时和非幂等 mutation 需要应用层处理语义边界 |
练习
- 在服务中把
Connection生命周期从请求级改为应用级,列出资源使用变化的观测指标。 - 对预分区表读取两个 row-key range,观察首次定位和后续读的 meta 访问差异。
- 在测试集群移动 Region,记录客户端刷新缓存的触发条件。
- 为
Increment设计超时重试策略,写出哪些失败不能简单重放。
系列导航
参考资料
- Apache HBase Reference Guide:Client、Catalog Tables、Regions、Architecture:https://hbase.apache.org/docs/
- Apache HBase 2.6 User API:
Connection、ConnectionFactory、Table、Admin、RegionLocator:https://hbase.apache.org/2.6/apidocs/ - Apache HBase 源码 rel/2.6.6:AsyncConnection、MetaCache、RegionLocator、client retry 相关实现:https://github.com/apache/hbase/tree/rel/2.6.6
- Apache HBase JIRA:client registry、meta locator、assignment 相关演进:https://issues.apache.org/jira/projects/HBASE
- Apache Hadoop 3.4.3 HDFS 文档:https://hadoop.apache.org/docs/r3.4.3/
