使用HBase Coprocessor协处理器

2024-08-27 11:48

本文主要是介绍使用HBase Coprocessor协处理器,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

原文:

http://www.zhyea.com/2017/04/13/using-hbase-coprocessor.html

HBase的Coprocessor是模仿谷歌BigTable的Coprocessor模型实现的。

Coprocessor提供了一种机制可以让开发者直接在RegionServer上运行自定义代码来管理数据。

首先必须要指明使用Coprocessor还是存在一些风险的。Coprocessor是HBase的高级功能,本来是只为HBase系统开发人员准备的。因为Coprocessor的代码直接在RegionServer上运行,并直接接触数据,这样就带来了数据破坏的风险,比如“中间人攻击(Man-in-the-MiddleAttack,简称“MITM攻击”,见百度词条)”以及其他类型的恶意入侵。目前还没有任何机制来屏蔽Coprocessor导致的数据破坏。此外,因为没有资源隔离,一个即使不是恶意设计的但表现不佳的Coprocessor也会严重影响集群的性能和稳定性。

通常我们访问HBase的方式是使用scan或get获取数据,使用Filter过滤掉不需要的部分,最后在获取到的数据上进行业务运算。但是在数据量非常大的时候,比如一个有上亿行及十万个列的数据集,再按常用的方式移动获取数据就会在网络层面遇到瓶颈。客户端也需要有强大的计算能力以及足够的内存来处理这么多的数据。此外,这也会使客户端的代码变得庞大而复杂。

这种场景正是Coprocessor可以发挥作用的地方。我们可以将业务运算代码封装到Coprocessor中并在RegionServer上运行,即在数据实际存储位置执行,最后将运算结果返回到客户端。

如下的一些理论可以帮助我们理解Coprocessor是如何发挥作用的:

触发器和存储过程:一个Observer Coprocessor有些类似于关系型数据库中的触发器,通过它我们可以在一些事件(如Get或是Scan)发生前后执行特定的代码。Endpoint Coprocessor则类似于关系型数据库中的存储过程,因为它允许我们在RegionServer上直接对它存储的数据进行运算,而非是在客户端完成运算。

MapReduce:MapReduce的原则就是将运算移动到数据所处的节点。Coprocessor也是按照相同的原则去工作的。

AOP:如果熟悉AOP的概念的话,可以将Coprocessor的执行过程视为在传递请求的过程中对请求进行了拦截,并执行了一些自定义代码。

Coprocessor类型

Coprocessor可以分为两大类:Observer Coprocessors(观察者)和EndPoint Coprocessor(终端)。

Observer Coprocessors

Observer Coprocessor在一个特定的事件发生前或发生后触发。在事件发生前触发的Coprocessor需要重写以pre作为前缀的方法,比如prePut。在事件发生后触发的Coprocessor使用方法以post作为前缀,比如postPut。

Observer Coprocessor的使用场景如下:

  • 安全性:在执行Get或Put操作前,通过preGet或prePut方法检查是否允许该操作;
  • 引用完整性约束:HBase并不直接支持关系型数据库中的引用完整性约束概念,即通常所说的外键。但是我们可以使用Coprocessor增强这种约束。比如根据业务需要,我们每次写入user表的同时也要向user_daily_attendance表中插入一条相应的记录,此时我们可以实现一个Coprocessor,在prePut方法中添加相应的代码实现这种业务需求。
  • 二级索引:可以使用Coprocessor来维持一个二级索引。这里暂不展开,有时间会单独说明。

根据作用的对象,Observer Coprocessor有如下几种:RegionObserver、RegionServerObserver、MasterObserver和WalObserver。我们可以通过这些Observer来处理其观察的对象的操作,比如可以通过RegionObserver处理Region相关的事件,如Get和Put操作。

Endpoint Coprocessor

Endpoint Coprocessor可以让开发者在数据本地执行运算。一个典型的案例:一个table有几百个Region,需要计算它的运行平均值或者总和。

Observer Coprocessor中代码的执行是相对透明的,而对于Endpoint Coprocessor,则需要显式的调用Table, HTableInterface或者HTable中的CoprocessorService()方法才能使之执行。

从0.96版本开始,HBase开始使用Google的protobuff。这对Endpoint Coprocessor的开发多少有一些影响。Endpoint Coprocessor不应该使用HBase内部成员,尽量只使用公共的API,最理想的情况应该是只依赖接口和数据结构。这样可以使开发的Endpoint Coprocessor更加健壮,不会受到HBase内核演进的干扰。注释为private或evolving的HBase内部API在删除前不必遵守关于deprecate的语义版本规则或相关的一般java规则。而使用protobuff生成的文件不会受到这些注释的影响,因为这些文件是用protoc工具自动生成的。在生成时这些文件时,protoc不知道也不会考虑HBase是如何工作的。

装载和卸载Coprocessor

要使用Coprocessor,就需要先完成对其的装载。这可以静态实现(通过HBase配置文件),也可以动态完成(通过shell或Java API)。

静态装载和卸载Coprocessor

按以下如下步骤可以静态装载自定义的Coprocessor。需要注意的是,如果一个Coprocessor是静态装载的,要卸载它就需要重启HBase。

静态装载步骤如下:

1. 在hbase-site.xml中使用<property>标签定义一个Coprocessor。<property>的子元素<name>的值只能从下面三个中选一个:

  • hbase.coprocessor.region.classes 对应 RegionObservers和Endpoints;
  • hbase.coprocessor.wal.classes 对应 WALObservers;
  • hbase.coprocessor.master.classes 对应MasterObservers。

而<value>标签的内容则是自定义Coprocessor的全限定类名。

下面演示了如何装载一个自定义Coprocessor(这里是在SumEndPoint.java中实现的),需要在每个RegionServer的hbase-site.xml中创建如下的记录:

如果要装载多个类,类名需要以逗号分隔。HBase会使用默认的类加载器加载配置中的这些类,因此需要将相应的jar文件上传到HBase服务端的类路径下。

使用这种方式加载的Coprocessor将会作用在HBase所有表的全部Region上,因此这样加载的Coprocessor又被称为系统Coprocessor。在Coprocessor列表中第一个Coprocessor的优先级值为Coprocessor.Priority.SYSTEM,其后的每个Coprocessor的值将会按序加一(这意味着优先级会减降低,因为优先级是按整数的自然顺序降序排列的)。

当调用配置的Observer Coprocessor时,HBase将会按照优先级顺序依次调用它们的回调方法。

2. 将代码放到HBase的类路径下。一个简单的方法是将封装好的jar(包括代码和依赖)放到HBase安装路径下的/lib目录中。

3. 重启HBase。

静态卸载的步骤如下:

1. 移除在hbase-site.xml中的配置。

2. 重启HBase。

3. 这一步是可选的,将上传到HBase类路径下的jar包移除。

动态装载Coprocessor

动态装载Coprocessor的一个优势就是不需要重启HBase。不过动态装载的Coprocessor只是针对某个表有效。因此,动态装载的Coprocessor又被称为表级Coprocessor。

此外,动态装载Coprocessor是对表的一次schema级别的调整,因此在动态装载Coprocessor时,目标表需要离线。

动态装载Coprocessor有两种方式:通过HBase Shell和通过Java API。

在下面介绍关于动态装载的部分,假设已经封装好了一个coprocessor.jar的包,里面包含实现代码及所有的依赖,并且已经将这个jar上传到了HDFS中。

通过HBase Shell动态装载和卸载

装载步骤如下

1. 在HBase Shell中disable 掉目标表

2. 使用类似如下的命令加载Coprocessor

简单解释下这个命令。这条命令在一个表的table_att中添加了一个新的属性“Coprocessor”。使用的时候Coprocessor会尝试从这个表的table_attr中读取这个属性的信息。这个属性的值用管道符“|”分成了四部分:

  • 文件路径:文件路径中需要包含Coprocessor的实现,并且对所有的RegionServer都是可达的。这个路径可以是每个RegionServer的本地磁盘路径,也可以是HDFS上的一个路径。通常建议是将Coprocessor实现存储到HDFS。HBASE-14548允许使用一个路径中包含的所有的jar,或者是在路径中使用通配符来指定某些jar,比如:hdfs://<namenode>:<port>/user/<hadoop-user>/ 或者 hdfs://<namenode>:<port>/user/<hadoop-user>/*.jar。需要注意的是如果是用路径来指定要加载的Coprocessor,这个路径下的所有jar文件都会被加载,不过该路径下的子目录中的jar不会被加载。另外,如果要用路径指定Coprocessor时,就不要再使用通配符了。这些特性在Java API中也得到了支持。
  • 类名:Coprocessor的全限定类名。
  • 优先级:一个整数。HBase将会使用优先级来决定在同一个位置配置的所有Observer Coprocessor的执行顺序。这个位置可以留白,这样HBase将会分配一个默认的优先级。
  • 参数(可选的):这些值会被传递给要使用的Coprocessor实现。这个项是可选的。

3. enable这个表

4. 检验Coprocessor是否被加载

Coprocessor可以在TABLE_ATTRIBUTES中找到。

加载步骤就是这样。

卸载步骤如下

1. disbale目标表

2. 使用alter命令移除掉Coprocessor

3. enable目标表

使用Java API动态装载和卸载

装载方式如下

针对不同版本的HBase会有不同的JavaAPI。幸运的是有一个全版本的Java API。下面的代码演示了是如何使用Java API来装载Coprocessor的:

0.96及更高版本的HBase还有另一套API。在这套API里,HTableDescriptor的addCoprocessor()方法提供了一种更简单的方式来动态加载Coprocessor:

卸载方式如下:

卸载方式就是重新加载表定义信息。重新加载的时候就不需要再使用setValue()方法或者是addCoprocessor()方法设置表的Coprocessor信息了:

对于0.96及更高版本的HBase,可以使用HTableDescriptor类的removeCoprocessor()方法。

Coprocessor示例程序

在写示例程序之前,先假设一个场景:我们有一张名为“users”的表,包含personalDet和salaryDet两个列族。这两个列族中分别记录了个人信息和薪资信息的详情。具体如下表:


personalDetsalaryDet
rowkeynamelastnamedobgrossnetallowances
adminAdminAdmin



cdickensCharlesDickens02/07/18121000080002000
jverneJulesVerne02/08/18281200090003000
Observer 实例

现在我们写一个Observer Coprocessor,目标是阻止在对users表进行scan或get时获取admin用户的信息。具体步骤如下:

  • 实现RegionObserver接口。
  • 重写preGetOp()方法(preGet()方法已经过时)检查客户端有没有查询行键为“admin”的记录。如果有的话直接返回空结果,没有的话,正常处理请求。
  • 将代码和依赖封装成一个jar。
  • 上传jar到HDFS——HBase需要能访问到这个HDFS。
  • 加载Coprocessor。
  • 编写测试程序进行测试。

下面是Coprocessor的实现:

重写preGetOp方法将只对Get操作生效,要对scan生效还需要重写preScannerOpen()方法来从scan结果中过滤掉“admin”的信息:

现在代码可以工作了,不过还存在一个问题:如果客户端在scan的时候也使用了Filter,客户端使用的Filter就会被这个FIlter覆盖掉。这不是一个好方法,所以我们可以在查询结果上做手脚,从查询结果中删除掉行键为“admin”的记录:

Endpoint实例

还是对users表进行处理。这次的目标是计算所有员工的薪资的总和。需要编写一个Endpoint Coprocessor,步骤如下:

1. 创建一个“.proto”文件定义服务

“.proto”是protobuff的对象描述文件,使用前需要先安装protobuff,目前使用的版本应该还是2.5版本。

2. 执行protoc命令,通过“.proto”文件生成Java代码

根据文件描述定义将会生成一个名为Sum.java的文件。

3. 编写一个Coprocessor类,实现Coprocessor和CoprocessorService两个接口,并实现接口中定义的方法:

4. 加载Coprocessor

5. 编写客户端代码调用Coprocessor

部署Coprocessor的原则

更新Coprocessor

更新动态部署的Coprocessor并不是简单地disable表,替换jar,然后重新启用Coprocessor。在JVM中,如果一个类还有引用,我们就无法重新加载它。因为当前的JVM对自定义的Coprocessor还有引用,要完成更新就需要重启JVM,也就是重启RegionSever。

关于日志

Coprocessor框架并没有提供日志相关的API。

注意事项

重复加载Coprocessor

如果我们先静态加载了一个Coprocessor,而后又通过HBase Shell动态加载了一次这个Coprocessor。那么先加载的Coprocessor并不会被覆盖,而是会同时存在两个Coprocessor实例。第二个Coprocessor会有更低的优先级,换句话说,重复加载的第二个Coprocessor实例实际上没有发挥作用。

################


这篇关于使用HBase Coprocessor协处理器的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1111585

相关文章

vue使用docxtemplater导出word

《vue使用docxtemplater导出word》docxtemplater是一种邮件合并工具,以编程方式使用并处理条件、循环,并且可以扩展以插入任何内容,下面我们来看看如何使用docxtempl... 目录docxtemplatervue使用docxtemplater导出word安装常用语法 封装导出方

Linux换行符的使用方法详解

《Linux换行符的使用方法详解》本文介绍了Linux中常用的换行符LF及其在文件中的表示,展示了如何使用sed命令替换换行符,并列举了与换行符处理相关的Linux命令,通过代码讲解的非常详细,需要的... 目录简介检测文件中的换行符使用 cat -A 查看换行符使用 od -c 检查字符换行符格式转换将

使用Jackson进行JSON生成与解析的新手指南

《使用Jackson进行JSON生成与解析的新手指南》这篇文章主要为大家详细介绍了如何使用Jackson进行JSON生成与解析处理,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 核心依赖2. 基础用法2.1 对象转 jsON(序列化)2.2 JSON 转对象(反序列化)3.

使用Python实现快速搭建本地HTTP服务器

《使用Python实现快速搭建本地HTTP服务器》:本文主要介绍如何使用Python快速搭建本地HTTP服务器,轻松实现一键HTTP文件共享,同时结合二维码技术,让访问更简单,感兴趣的小伙伴可以了... 目录1. 概述2. 快速搭建 HTTP 文件共享服务2.1 核心思路2.2 代码实现2.3 代码解读3.

Elasticsearch 在 Java 中的使用教程

《Elasticsearch在Java中的使用教程》Elasticsearch是一个分布式搜索和分析引擎,基于ApacheLucene构建,能够实现实时数据的存储、搜索、和分析,它广泛应用于全文... 目录1. Elasticsearch 简介2. 环境准备2.1 安装 Elasticsearch2.2 J

使用C#代码在PDF文档中添加、删除和替换图片

《使用C#代码在PDF文档中添加、删除和替换图片》在当今数字化文档处理场景中,动态操作PDF文档中的图像已成为企业级应用开发的核心需求之一,本文将介绍如何在.NET平台使用C#代码在PDF文档中添加、... 目录引言用C#添加图片到PDF文档用C#删除PDF文档中的图片用C#替换PDF文档中的图片引言在当

Java中List的contains()方法的使用小结

《Java中List的contains()方法的使用小结》List的contains()方法用于检查列表中是否包含指定的元素,借助equals()方法进行判断,下面就来介绍Java中List的c... 目录详细展开1. 方法签名2. 工作原理3. 使用示例4. 注意事项总结结论:List 的 contain

C#使用SQLite进行大数据量高效处理的代码示例

《C#使用SQLite进行大数据量高效处理的代码示例》在软件开发中,高效处理大数据量是一个常见且具有挑战性的任务,SQLite因其零配置、嵌入式、跨平台的特性,成为许多开发者的首选数据库,本文将深入探... 目录前言准备工作数据实体核心技术批量插入:从乌龟到猎豹的蜕变分页查询:加载百万数据异步处理:拒绝界面

Android中Dialog的使用详解

《Android中Dialog的使用详解》Dialog(对话框)是Android中常用的UI组件,用于临时显示重要信息或获取用户输入,本文给大家介绍Android中Dialog的使用,感兴趣的朋友一起... 目录android中Dialog的使用详解1. 基本Dialog类型1.1 AlertDialog(

Python使用自带的base64库进行base64编码和解码

《Python使用自带的base64库进行base64编码和解码》在Python中,处理数据的编码和解码是数据传输和存储中非常普遍的需求,其中,Base64是一种常用的编码方案,本文我将详细介绍如何使... 目录引言使用python的base64库进行编码和解码编码函数解码函数Base64编码的应用场景注意