环境信息:
HBase 1.2.2,Hadoop 2.7.2
使用需求:
由于需要使用es创建hbase的二级索引,所以打算使用coprocessor中的RegionObserver来实现此功能,本文主要也是讲解RegionObserver的加载以及使用注意事项
使用总结:
RegionObserver的加载分为动态加载和静态加载
静态加载(通过配置HBase):
在 hbase-site.xml 定义一个协处理器
<property>
<name>hbase.coprocessor.region.classes</name>
<value>org.myname.hbase.coprocessor.endpoint.SumEndPoint</value>
</property>
如果多个类被指定要加载,类名间要用逗号分隔。框架会试图使用默认类加载器加载所有配置的类。因此,这个jar文件必须位于server端的HBase classpath中。
通过这种方式加载的Coprocessors将对所有表的所有Region都是激活状态的。这些也被称作 系统协处理器System Coprocessor。首先列出的Coprocessors 将被赋予 Coprocessor.Priority.SYSTEM这样的优先级。列表中每一个后续的
coprocessor 将会把它的优先级加一(这是在减小它的优先级,因为优先级是按照整数的自然顺序排列的,第一个coprocessor被赋值为coprocessor$1,后续$后面的数据会递增)。
当调用注册的观察者时,框架会按照它们的优先级顺序执行它们的回调方法。
静态卸载:
-
在hbase-site.xml中删除协处理器的 <property> 元素,包括子元素。
-
重启 HBase.
-
可选的操作是,从HBase的 lib/ 目录下或者从classpath中移除这个协处理器的 JAR 文件。
动态加载(使用 HBase Shell 或 Java API):
你可以在不重启HBase的情况下,动态加载协处理器。这看起来比静态加载更好,但是动态加载协处理器的是被加载到一张表上,并且只在加载它们的表上可用。由此,动态加载的协处理器有时被称为表协处理器Table Coprocessor。
另外,动态加载一个协处理器相当于改变表的结构,表必须下线以加载协处理器。
准备工作:
-
一个JAR
coprocessor.jar
包含了这个 Coprocessor 的执行和它所有的依赖(rich package)。 -
这个JAR 在 HDFS 中可用,如
hdfs://<namenode>:<port>/path/coprocessor.jar
HBase shell:
添加:
-
在HBase Shell中禁用表:
hbase(main):001:0> disable 'ais_ori_2018'
-
使用如下命令加载这个 Coprocessor:
hbase(main):002:0> alter 'ais_ori_2018', METHOD => 'table_att', 'coprocessor' => 'hdfs:///coprocessor/coprocessor4es-0.0.2-SNAPSHOT.jar|com.hxy.main.HbaseDataSyncEsObserver|1001|es_cluster=cetcocean-es,es_type=indexer,es_index=hbase_indexer_alias,es_port=9300,es_host=192.168.10.48'
协处理器框架将试着从协处理器表的属性值中读取类的信息。这个值包含由管道符号(|)分割成的4片信息。
-
文件路径: 包含 Coprocessor 定义的这个jar文件必须在所有的RegionServer可以读取到的位置上。
你可以拷贝这个文件到每一个RegionServer的本地磁盘上,但是推荐把它存放在HDFS上。 -
类名: Coprocessor的完整类名.
-
优先级: 一个整数。该框架将确定在同一个钩子上使用优先级注册的所有配置的观察器的执行顺序,并使用优先级。这片信息可以留空不填,这样的话,框架将会赋予一个默认的优先级值。
-
参数 (可选): 这片信息会传入协处理器的实现类中。这片信息是可选的。
-
-
启用这个表.
hbase(main):003:0> enable 'ais_ori_2018'
-
验证协处理器是否被加载:
hbase(main):04:0> describe 'ais_ori_2018'
协处理器应该在
TABLE_ATTRIBUTES
列出。
卸载:
-
禁用表
hbase(main):001:0> disable 'ais_ori_2018'
-
修改表以移除coprocessor.
hbase(main):002:0> alter 'users', METHOD => 'table_att_unset', NAME => 'coprocessor$1'
启用表
hbase(main):003:0> enable 'ais_ori_2018'
Java API:
添加:
过程就是修改表结构,其中增加一个属性即可
TableName tableName = TableName.valueOf("ais_ori_2018");
String path = "hdfs://<namenode>:<port>/path/coprocessor.jar";
Configuration conf = HBaseConfiguration.create();
Connection connection = ConnectionFactory.createConnection(conf);
Admin admin = connection.getAdmin();
admin.disableTable("ais_ori_2018");
HTableDescriptor hTableDescriptor = new HTableDescriptor("ais_ori_2018");
HColumnDescriptor columnDesc = new HColumnDescriptor(Bytes.toBytes("data"));
columnDesc.setMaxVersions(3);
columnDesc.setBlockCacheEnabled(true);
columnDesc.setBloomFilterType(BloomType.ROW);
columnDesc.setCompressionType(Algorithm.SNAPPY);
hTableDescriptor.addFamily(columnDesc);
hTableDescriptor.setValue("COPROCESSOR$1", path + "|" + RegionObserverExample.class.getCanonicalName() + "|" + Coprocessor.PRIORITY_USER);
admin.modifyTable("ais_ori_2018", hTableDescriptor);
admin.enableTable("ais_ori_2018");
卸载:
将刚才添加的属性去掉覆盖即可
TableName tableName = TableName.valueOf("ais_ori_2018");
String path = "hdfs://<namenode>:<port>/path/coprocessor.jar";
Configuration conf = HBaseConfiguration.create();
Connection connection = ConnectionFactory.createConnection(conf);
Admin admin = connection.getAdmin();
admin.disableTable("ais_ori_2018");
HTableDescriptor hTableDescriptor = new HTableDescriptor("ais_ori_2018");
HColumnDescriptor columnDesc = new HColumnDescriptor(Bytes.toBytes("data"));
columnDesc.setMaxVersions(3);
columnDesc.setBlockCacheEnabled(true);
columnDesc.setBloomFilterType(BloomType.ROW);
columnDesc.setCompressionType(Algorithm.SNAPPY);
hTableDescriptor.addFamily(columnDesc);
admin.modifyTable("ais_ori_2018", hTableDescriptor);
admin.enableTable("ais_ori_2018");
问题总结:
捆绑协处理器 Bundling Coprocessors
你可以将一个coprocessor的所有类捆绑到RegionServer的classpath中的单个JAR中,以便进行简单的部署。否则,将所有依赖项放在RegionServer的classpath中,以便在RegionServer启动时加载它们。
更新一个协处理器 Updating a Coprocessor
部署一个给定的coprocessor的新版本并不像禁用它、替换JAR并重新启用协处理器那样简单。这是因为你不能在JVM中重新加载一个类(通过反射来进行加载coprocessor的主类),除非你删除了所有的对它当前的引用。既然当前JVM引用了这个已经存在的协处理器,你必须通过重启RegionServer来重启这个JVM,以替代这个协处理器。这种行为是不可能改变的。(划重点:如果想不重启hbase而更新Coprocessor,可以修改Coprocessor的包名来实现,这样jvm就会重新加载这个类达到更新的目的)
协处理器日志 Coprocessor Logging
Coprocessor框架不提供超出标准Java日志记录的API。Coprocessor的日志存放在Regionserver的日志目录下,在不同的region上执行了Coprocessor,会将日志记录在该region对应的Regionserver的日志目录下
协处理器配置 Coprocessor Configuration
针对写es作为hbase二级索引的场景建议将es的相关配置放在coprocessor配置的第四个参数上,可以做到动态配置,涉及到coprocessor使用外部静态配置的场景可以尝试将配置写在此处然后在代码的configuration对象中获取。
协处理器配置容错配置
由于coprocessor是部署在region上的,所以coprocessor如果抛出异常会影响到regionserver的正常运行,源码如下:
/**
* This is used by coprocessor hooks which are declared to throw IOException
* (or its subtypes). For such hooks, we should handle throwable objects
* depending on the Throwable's type. Those which are instances of
* IOException should be passed on to the client. This is in conformance with
* the HBase idiom regarding IOException: that it represents a circumstance
* that should be passed along to the client for its own handling. For
* example, a coprocessor that implements access controls would throw a
* subclass of IOException, such as AccessDeniedException, in its preGet()
* method to prevent an unauthorized client's performing a Get on a particular
* table.
* @param env Coprocessor Environment
* @param e Throwable object thrown by coprocessor.
* @exception IOException Exception
*/
protected void handleCoprocessorThrowable(final CoprocessorEnvironment env, final Throwable e)
throws IOException {
if (e instanceof IOException) {
throw (IOException)e;
}
// If we got here, e is not an IOException. A loaded coprocessor has a
// fatal bug, and the server (master or regionserver) should remove the
// faulty coprocessor from its set of active coprocessors. Setting
// 'hbase.coprocessor.abortonerror' to true will cause abortServer(),
// which may be useful in development and testing environments where
// 'failing fast' for error analysis is desired.
if (env.getConfiguration().getBoolean(ABORT_ON_ERROR_KEY, DEFAULT_ABORT_ON_ERROR)) {
// server is configured to abort.
abortServer(env, e);
} else {
LOG.error("Removing coprocessor '" + env.toString() + "' from " +
"environment because it threw: " + e,e);
coprocessors.remove(env);
try {
shutdown(env);
} catch (Exception x) {
LOG.error("Uncaught exception when shutting down coprocessor '"
+ env.toString() + "'", x);
}
throw new DoNotRetryIOException("Coprocessor: '" + env.toString() +
"' threw: '" + e + "' and has been removed from the active " +
"coprocessor set.", e);
}
}
所以,如果添加coprocessor的时候打算重启HBase,建议增加配置项来防止协处理器出现错误时导致regionServer挂掉
<property>
<name>hbase.coprocessor.abortonerror</name>
<value>false</value>
</property>