前言
本文隶属于专栏《大数据技术体系》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢!
本专栏目录结构和参考文献请见大数据技术体系
Coprocessor
HBase 使用 Coprocessor 机制,使用户可以将自己编写的程序运行在 RegionServer上。
大多数情况下 HBase 用户并不需要这个功能,通过调用 HBase 提供的读写 API 或者使用 Bulkload 功能基本上可以满足日常的业务需求。
但在部分特殊应用场景下,使用 Coprocessor 可以大幅提升业务的执行效率。
比如,业务需要从 HBase 集群加载出来几十亿行数据进行求和运算或是求平均值运算,如果调用 HBase API 将数据全部扫描出来在客户端进行计算,势必有下列问题:
- 大量数据传输可能会成为瓶颈,这就导致整个业务的执行效率可能受限于数据的传输效率。
- 客户端内存可能会因为无法存储如此大量的数据而 OOM 。
- 大量数据传输可能将集群带宽耗尽,严重影响集群中其他业务的正常读写。
这种场景下,如果能够将客户端的计算代码迁移到 RegionServer 服务器端执行,就能很好地避免上述问题,在保证不影响其他业务的情况下提升计算效率。
Coprocessor 分类
HBaseCoprocessor 分为两种: Observer 和 Endpoint ,如下图所示。
Observer Coprocessor 类似于 MySQL 中的触发器。
Observer Coprocessor 提供钩子使用户代码在特定事件发生之前或者之后得到执行。
比如,想在调用 get 方法之前执行你的代码逻辑,可以重写如下方法:
public void preGetOp(ObserverContext<RegionCoprocessorEnvironment> e, Get get, List<Cell> results) throws IOException
同理,如果想在调用 get 方法之后执行你的代码逻辑,可以重写 postGet 方法:
public void postGetOp(ObserverContext<RegionCoprocessorEnvironment> e, Get get, List<Cell> results) throws IOException
下图以 preGet 和 postGet 为例说明 ObserverCoprocessor 与 RegionServer 中其他组件的协作关系。
使用 Observer Coprocessor 的最典型案例是在执行 put 或者 get 等操作之前检查用户权限。
在当前 HBase 系统中,提供了 4 种 Observer 接口:
- RegionObserver,主要监听 Region 相关事件,比如 get 、 put 、 scan 、 delete 以及 flush 等。
- RegionServerObserver ,主要监听 RegionServer 相关事件,比如 RegionServer 启动、关闭,或者执行 Region 合并等事件。
- WALObserver ,主要监听 WAL 相关事件,比如 WAL 写入、滚动等。
- MasterObserver ,主要监听 Master 相关事件,比如建表、删表以及修改表结构等。
EndpointCoprocessor 类似于 MySQL 中的存储过程。
EndpointCoprocessor 允许将用户代码下推到数据层执行。
一个典型的例子就是计算一张表(设计大量 Region ) 的平均值或者求和,可以使用 EndpointCoprocessor 将计算逻辑下推到 RegionServer 执行。
通过 EndpointCoprocessor ,用户可以自定义一个客户端与 RegionServer 通信的 RPC 调用协议,通过 RPC 调用执行部署在服务器端的业务代码。
需要注意的是, ObserverCoprocessor 执行对用户来说是透明的,只要 HBase 系统执行了 get 操作,对应的 preGetOp 就会得到执行,不需要用户显式调用 preGetOp 方法;而 EndpointCoprocessor 执行必须由用户显式触发调用。
Coprocessor 加载
用户定义的 Coprocessor 可以通过两种方式加载到 RegionServer ;一种是通过配置文件静态加载;一种是动态加载。
1 .静态加载
通过静态加载的方式将 Coprocessor 加载到集群需要执行以下 3 个步骤。
- 将 Coprocessor 配置到 hbase-site.xml。 hbase-site.xml 中定义了多个相关的配置项:
配置 | Observer |
---|---|
hbase.coprocessor.region.classes | RegionObservers 和 EndpointCoprocessor |
hbase.coprocessor.wal.classes | WALObservers |
hbase.coprocessor.master.classe | MasterObservers |
比如业务实现一个 EndpointCoprocessor ,需要按如下方式配置到 hbase-site.xml:
<property>
<name>hbase.coprocessor.region.classes</name>
<value>com.shockang.study.hbase.coprocessor.endpoint.SumEndPoint</value>
</property>
- 将 Coprocessor 代码放到 HBase 的 classpath 下。最简单的方法是将 Coprocessor 对应的 jar 包放在 HBase lib 目录下。
- 重启 HBase 集群。
2. 动态加载
静态加载 Coprocessor 需要重启集群,使用动态加载方式则不需要重启。 动态加载当前主要有 3 种方式。
方式 1:使用 shell
- disable 表,代码如下:
hbase(main):001:0> disable 'users'
- 修改表 schema ,代码如下:
hbase(main):002:0> alter 'users', METHOD =>'table_att','Coprocessor'=>'hdfs://.../coprocessor.jar| com.shockang.study.hbase.Coprocessor.RegionobserverExample "/"
其中, hdfs://.../coprocessor.jar
表示 Coprocessor 代码部署在 HDFS 的具体路径, com.shockang.study.hbase.Coprocessor.RegionobserverExample
是执行类的全称。
- enable 表,代码如下:
hbase(main):003:0> enable 'users'
方式2:使用HTableDescriptor 的 setValue() 方法
代码如下:
String path="hdfs://.../coprocessor.jar";
HTableDescriptor hTableDescriptor = new HTableDescriptor(tableName);
hTableDescriptor.setvalue("COPROCESSORS1",path + "/" RegionObserverExample.class.getCanonicalName()+"/" Coprocessor.PRIORITY_USER);
方式3:使用 HTableDescriptor 的 addCoprocessor() 方法
代码如下:
Path path = new Path("hdfs://coprocessor_path");
TableDescriptor hTableDescriptor = new HTableDescriptor(tableName);
hTableDescriptor.addCoprocessor(RegionobserverExample.class.getCanonicalName(), path, Coprocessor.PRIORITY_USER, null);