1. 简介

    对于HBase的协处理器概念可由其官方博文了解:https://blogs.apache.org/hbase/entry/coprocessor_introduction

  总体来说其包含两种协处理器:Observers和Endpoint。

  其中Observers可以理解问传统数据库的触发器,当发生某一个特定操作的时候出发Observer。

  1. RegionObserver:提供基于表的region上的Get, Put, Delete, Scan等操作,比如可以在客户端进行get操作的时候定义RegionObserver来查询其时候具有get权限等。具体的方法(拦截点)有:

    1. preOpen, postOpen: Called before and after the region is reported as online to the master.
    2. preFlush, postFlush: Called before and after the memstore is flushed into a new store file.
    3. preGet, postGet: Called before and after a client makes a Get request.
    4. preExists, postExists: Called before and after the client tests for existence using a Get.
    5. prePut and postPut: Called before and after the client stores a value.
    6. preDelete and postDelete: Called before and after the client deletes a value.
  2. WALObserver:提供基于WAL的写和刷新WAL文件的操作,一个regionserver上只有一个WAL的上下文。具体的方法(拦截点)有:
    1. preWALWrite/postWALWrite: called before and after a WALEdit written to WAL.
  3. MasterObserver:提供基于诸如ddl的的操作检查,如create, delete, modify table等,同样的当客户端delete表的时候通过逻辑检查时候具有此权限场景等。其运行于Master进程中。具体的方法(拦截点)有:
    1. preCreateTable/postCreateTable: Called before and after the region is reported as online to the master.
    2. preDeleteTable/postDeleteTable

    以上对于Observer的逻辑以RegionObserver举例来说其时序图如下:

  其中Endpoint可以理解为传统数据库的存储过程操作,比如可以进行某族某列值得加和。无Endpoint特性的情况下需要全局扫描表,通过Endpoint则可以在多台

  分布有对应表的regionserver上同步加和,在江加和数返回给客户端进行全局加和操作,充分利用了集群资源,增加性能。Endpoint基本概念如下图:

2. 两者代码实现细节的差异

  在实现两种协处理器的时候稍有区别。无论哪种协处理器都需要运行于Server端的环境中。其中Endpoint还需要通过protocl来定义接口实现客户端代码进行rpc通信以此来进行数据的搜集归并。而Observer则不需要客户端代码,只在特定操作发生的时候出发服务端代码的实现。

3. Observer协处理器的实现

  相对来说Observer的实现来的简单点,只需要实现服务端代码逻辑即可。通过实现一个RegionserverObserver来加深了解。

  所要实现的功能:

  1. 假定某个表有AB两个列--------------------------------便于后续描述我们称之为coprocessor_table
  2. . 当我们向A列插入数据的时候通过协处理器像B列也插入数据。
  3. .在读取数据的时候只允许客户端读取B列数据而不能读取A列数据。换句话说A列是只写 B列是只读的。(为了简单起见,用户在读取数据的时候需要制定列名)
  4. . A列值必须是整数,换句话说B列值也自然都是整数
  5.  
  6. .当删除操作的时候不能指定删除B
  7. .当删除A列的时候同时需要删除B
  8. .对于其他列的删除不做检查

在上述功能点确定后,我们就要开始实现这两个功能。好在HBase API中有BaseRegionObserver,这个类已经帮助我们实现了大部分的默认实现,我们只要专注于业务上的方法重载即可。

代码框架:  

  1. public class 协处理器类名称 extends BaseRegionObserver {
  2. private static final Log LOG = LogFactory.getLog(协处理器类名称.class);
  3. private RegionCoprocessorEnvironment env = null;// 协处理器是运行于region中的,每一个region都会加载协处理器
  4. // 这个方法会在regionserver打开region时候执行(还没有真正打开)
  5. @Override
  6. public void start(CoprocessorEnvironment e) throws IOException {
  7. env = (RegionCoprocessorEnvironment) e;
  8. }
  9.  
  10. // 这个方法会在regionserver关闭region时候执行(还没有真正关闭)
  11. @Override
  12. public void stop(CoprocessorEnvironment e) throws IOException {
  13. // nothing to do here
  14. }
  15.  
  16. /**
  17. * 出发点,比如可以重写prePut postPut等方法,这样就可以在数据插入前和插入后做逻辑控制了。
  18. */
  19. @Override

业务代码实现 :

  根据上述需求和代码框架,具体逻辑实现如下。

  • 在插入需要做检查所以重写了prePut方法
  • 在删除前需要做检查所以重写了preDelete方法
  1. public class MyRegionObserver extends BaseRegionObserver {
  2. private static final Log LOG = LogFactory.getLog(MyRegionObserver.class);
  3.  
  4. private RegionCoprocessorEnvironment env = null;
  5. // 设定只有F族下的列才能被操作,且A列只写,B列只读。的语言
  6. private static final String FAMAILLY_NAME = "F";
  7. private static final String ONLY_PUT_COL = "A";
  8. private static final String ONLY_READ_COL = "B";
  9.  
  10. // 协处理器是运行于region中的,每一个region都会加载协处理器
  11. // 这个方法会在regionserver打开region时候执行(还没有真正打开)
  12. @Override
  13. public void start(CoprocessorEnvironment e) throws IOException {
  14. env = (RegionCoprocessorEnvironment) e;
  15. }
  16.  
  17. // 这个方法会在regionserver关闭region时候执行(还没有真正关闭)
  18. @Override
  19. public void stop(CoprocessorEnvironment e) throws IOException {
  20. // nothing to do here
  21. }
  22.  
  23. /**
  24. * 需求 1.不允许插入B列 2.只能插入A列 3.插入的数据必须为整数 4.插入A列的时候自动插入B列
  25. */
  26. @Override
  27. public void prePut(final ObserverContext<RegionCoprocessorEnvironment> e,
  28. final Put put, final WALEdit edit, final Durability durability)
  29. throws IOException {
  30.  
  31. // 首先查看单个put中是否有对只读列有写操作
  32. List<Cell> cells = put.get(Bytes.toBytes(FAMAILLY_NAME),
  33. Bytes.toBytes(ONLY_READ_COL));
  34. if (cells != null && cells.size() != 0) {
  35. LOG.warn("User is not allowed to write read_only col.");
  36. throw new IOException("User is not allowed to write read_only col.");
  37. }
  38.  
  39. // 检查A列
  40. cells = put.get(Bytes.toBytes(FAMAILLY_NAME),
  41. Bytes.toBytes(ONLY_PUT_COL));
  42. if (cells == null || cells.size() == 0) {
  43. // 当不存在对于A列的操作的时候则不做任何的处理,直接放行即可
  44. LOG.info("No A col operation, just do it.");
  45. return;
  46. }
  47.  
  48. // 当A列存在的情况下在进行值得检查,查看是否插入了整数
  49. byte[] aValue = null;
  50. for (Cell cell : cells) {
  51. try {
  52. aValue = CellUtil.cloneValue(cell);
  53. LOG.warn("aValue = " + Bytes.toString(aValue));
  54. Integer.valueOf(Bytes.toString(aValue));
  55. } catch (Exception e1) {
  56. LOG.warn("Can not put un number value to A col.");
  57. throw new IOException("Can not put un number value to A col.");
  58. }
  59. }
  60.  
  61. // 当一切都ok的时候再去构建B列的值,因为按照需求,插入A列的时候需要同时插入B列
  62. LOG.info("B col also been put value!");
  63. put.addColumn(Bytes.toBytes(FAMAILLY_NAME),
  64. Bytes.toBytes(ONLY_READ_COL), aValue);
  65. }
  66.  
  67. /**
  68. * 需求 1.不能删除B列 2.只能删除A列 3.删除A列的时候需要一并删除B列
  69. */
  70. @Override
  71. public void preDelete(
  72. final ObserverContext<RegionCoprocessorEnvironment> e,
  73. final Delete delete, final WALEdit edit, final Durability durability)
  74. throws IOException {
  75.  
  76. // 首先查看是否对于B列进行了指定删除
  77. List<Cell> cells = delete.getFamilyCellMap().get(
  78. Bytes.toBytes(FAMAILLY_NAME));
  79. if (cells == null || cells.size() == 0) {
  80. // 如果客户端没有针对于FAMAILLY_NAME列族的操作则不用关心,让其继续操作即可。
  81. LOG.info("NO F famally operation ,just do it.");
  82. return;
  83. }
  84.  
  85. // 开始检查F列族内的操作情况
  86. byte[] qualifierName = null;
  87. boolean aDeleteFlg = false;
  88. for (Cell cell : cells) {
  89. qualifierName = CellUtil.cloneQualifier(cell);
  90.  
  91. // 检查是否对B列进行了删除,这个是不允许的
  92. if (Arrays.equals(qualifierName, Bytes.toBytes(ONLY_READ_COL))) {
  93. LOG.info("Can not delete read only B col.");
  94. throw new IOException("Can not delete read only B col.");
  95. }
  96.  
  97. // 检查是否存在对于A队列的删除
  98. if (Arrays.equals(qualifierName, Bytes.toBytes(ONLY_PUT_COL))) {
  99. LOG.info("there is A col in delete operation!");
  100. aDeleteFlg = true;
  101. }
  102. }
  103.  
  104. // 如果对于A列有删除,则需要对B列也要删除
  105. if (aDeleteFlg)
  106. {
  107. LOG.info("B col also been deleted!");
  108. delete.addColumn(Bytes.toBytes(FAMAILLY_NAME), Bytes.toBytes(ONLY_READ_COL));
  109. }
  110. }
  111.  
  112. }

4. Observer协处理器上传加载

  完成实现后需要将协处理器类打包成jar文件,对于协处理器的加载通常有三种方法:

  1.配置文件加载:即通过hbase-site.xml文件配置加载,一般这样的协处理器是系统级别的,全局的协处理器,如权限控制等检查。

  2.shell加载:可以通过alter命令来对表进行scheme进行修改来加载协处理器。

  3.通过API代码实现:即通过API的方式来加载协处理器。

上述加载方法中,1,3都需要将协处理器jar文件放到集群的hbase的classpath中。而2方法只需要将jar文件上传至集群环境的hdfs即可。

下面我们只介绍如何通过2方法进行加载。

  步骤1:通过如下方法创建表

  1. hbase(main):001:0> create 'coprocessor_table','F'
  2. 0 row(s) in 2.7570 seconds
  3.  
  4. => Hbase::Table - coprocessor_table

  步骤2:通过alter命令将协处理器加载到表中

  1. alter 'coprocessor_table' , METHOD =>'table_att','coprocessor'=>'hdfs://ns1/testdata/Test-HBase-Observer.jar|cn.com.newbee.feng.MyRegionObserver|1001'

  其中:'coprocessor'=>'jar文件在hdfs上的绝对路径|协处理器主类|优先级|协处理器参数

  上述协处理器并没有参数,所以未给出参数,对于协处理器的优先级不在此做讨论。

  步骤3:检查协处理器的加载

  1. hbase(main):021:0> describe 'coprocessor_table'
  2. Table coprocessor_table is ENABLED
  3. coprocessor_table, {TABLE_ATTRIBUTES => {coprocessor$1 => 'hdfs://ns1/testdata/T
  4. est-HBase-Observer.jar|cn.com.newbee.feng.MyRegionObserver|1001'}
  5. COLUMN FAMILIES DESCRIPTION
  6. {NAME => 'F', DATA_BLOCK_ENCODING => 'NONE', BLOOMFILTER => 'ROW', REPLICATION_S
  7. COPE => '0', VERSIONS => '1', COMPRESSION => 'NONE', MIN_VERSIONS => '0', TTL =>
  8. 'FOREVER', KEEP_DELETED_CELLS => 'FALSE', BLOCKSIZE => '65536', IN_MEMORY => 'f
  9. alse', BLOCKCACHE => 'true'}
  10. 1 row(s) in 0.0300 seconds

  可以看到协处理器被表成功加载,其实内部是将update所有此表的region去加载协处理器的。

5. Observer协处理器测试

  经过上述代码完成和加载完成后我们进行简单的协处理器测试,由于Observer并不需要我们去定制客户端代码,所以我们可以直接通过shell命令环境来测试。

  用例1: 正常插入A列

  1. hbase(main):024:0> scan 'coprocessor_table'
  2. ROW COLUMN+CELL
  3. 0 row(s) in 0.0100 seconds
  4.  
  5. hbase(main):025:0> put 'coprocessor_table','row1','F:A',123
  6. 0 row(s) in 0.0210 seconds
  7.  
  8. hbase(main):026:0> scan 'coprocessor_table'
  9. ROW COLUMN+CELL
  10. row1 column=F:A, timestamp=1469838240645, value=123
  11. row1 column=F:B, timestamp=1469838240645, value=123
  12. 1 row(s) in 0.0180 seconds

  结果:B列也被插入,OK

  

  用例2:插入A列,但是值不为整数

  1. hbase(main):027:0> put 'coprocessor_table','row1','F:A','cc'
  2.  
  3. ERROR: Failed 1 action: IOException: 1 time,

  结果:插入失败,服务端报如下错误,OK

  1. 2016-07-29 20:25:45,406 WARN [B.defaultRpcServer.handler=3,queue=0,port=60020] feng.MyRegionObserver: Can not put un number value to A col.

  用例3:插入B列

  1. hbase(main):028:0> put 'coprocessor_table','row1','F:B',123
  2.  
  3. ERROR: Failed 1 action: IOException: 1 time,

  结果:插入失败,服务器报如下错误,OK

  1. 2016-07-29 20:27:13,342 WARN [B.defaultRpcServer.handler=20,queue=2,port=60020] feng.MyRegionObserver: User is not allowed to write read_only col.

  用例4:删除B列

  1. hbase(main):029:0> delete 'coprocessor_table','row1','F:B'
  2.  
  3. ERROR: java.io.IOException: Can not delete read only B col.

  结果:删除失败,OK

  

  用例4:删除A列

  1. hbase(main):030:0> scan 'coprocessor_table'
  2. ROW COLUMN+CELL
  3. row1 column=F:A, timestamp=1469838240645, value=123
  4. row1 column=F:B, timestamp=1469838240645, value=123
  5. 1 row(s) in 0.0230 seconds
  6.  
  7. hbase(main):031:0> delete 'coprocessor_table','row1','F:A'
  8. 0 row(s) in 0.0060 seconds
  9.  
  10. hbase(main):032:0> scan 'coprocessor_table'
  11. ROW COLUMN+CELL
  12. 0 row(s) in 0.0070 seconds

  结果:A列和B列同时被删除了。

6. Observer协处理器总结

  Observer协处理器也可以看作为服务端的拦截器,用户可以根据需求确定拦截点,在去重写这些拦截点对应的方法即可,整个过程中不需要重启集群,在不修改HBase内部代码的情况下对HBase扩展更加方便。如扩展权限,扩展索引等都非常有用。

参考:

  HBase 协处理器编程详解第一部分:Server 端代码编写

  Coprocessor Introduction

代码下载:

  https://github.com/xufeng79x/Test-HBase-Observer

 

[How to] 使用HBase协处理器---基本概念和regionObserver的简单实现的更多相关文章

  1. HBase 协处理器---基本概念和regionObserver的简单实现

    1. 简介 对于HBase的协处理器概念可由其官方博文了解:https://blogs.apache.org/hbase/entry/coprocessor_introduction 总体来说其包含两 ...

  2. [How to] 使用HBase协处理器---Endpoint服务端的实现

    1.简介 前篇文章[How to] 使用HBase协处理器---基本概念和regionObserver的简单实现中提到了两种不同的协处理器,并且实现了regionObserver. 本文将介绍如何使用 ...

  3. HBase 协处理器编程详解第一部分:Server 端代码编写

    Hbase 协处理器 Coprocessor 简介 HBase 是一款基于 Hadoop 的 key-value 数据库,它提供了对 HDFS 上数据的高效随机读写服务,完美地填补了 Hadoop M ...

  4. HBase 协处理器编程详解,第二部分:客户端代码编写

    实现 Client 端代码 HBase 提供了客户端 Java 包 org.apache.hadoop.hbase.client.coprocessor.它提供以下三种方法来调用协处理器提供的服务: ...

  5. Hbase学习之概念与原理

    一.hbase与列式存储 hbase最早起源于谷歌的一篇BigTable的论文,它是由java编写的.开源的一个nosql数据库,同时它也是一个列式存储的.支持分布式(基于hdfs)的数据库.什么是列 ...

  6. [How to] 使用HBase协处理器---Endpoint客户端代码的实现

    1.简介 不同于Observer协处理器,EndPoint由于需要同region进行rpc服务的通信,以及客户端出数据的归并,需要自行实现客户端代码. 基于[How to] 使用HBase协处理器-- ...

  7. HBase协处理器的使用(添加Solr二级索引)

    给HBase添加一二级索引,HBase协处理器结合solr 代码如下 package com.hbase.coprocessor; import java.io.IOException; import ...

  8. HBase协处理器同步二级索引到Solr

    一. 背景二. 什么是HBase的协处理器三. HBase协处理器同步数据到Solr四. 添加协处理器五. 测试六. 协处理器动态加载 一. 背景 在实际生产中,HBase往往不能满足多维度分析,我们 ...

  9. HBase 学习之路(八)——HBase协处理器

    一.简述 在使用HBase时,如果你的数据量达到了数十亿行或数百万列,此时能否在查询中返回大量数据将受制于网络的带宽,即便网络状况允许,但是客户端的计算处理也未必能够满足要求.在这种情况下,协处理器( ...

随机推荐

  1. Contest 1

    A:注意到模数是要求lcm的数的倍数,直接先取模就可以了.考场脑抽,对其质因数分解判了一下每个因子有没有,当然也行. #include<iostream> #include<cstd ...

  2. gpart 分区工具

    gpart 分区工具 https://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/disk-organization.html Table 3 ...

  3. EVE-NG FAQ

    EVE-NG FAQ How to install EVE on bare box using Ubuntuoriginal ISO distro. Get Ubuntu ISO: https://w ...

  4. 洛谷P4606 [SDOI2018]战略游戏 【圆方树 + 虚树】

    题目链接 洛谷P4606 双倍经验:弱化版 题解 两点之间必经的点就是圆方树上两点之间的圆点 所以只需建出圆方树 每次询问建出虚树,统计一下虚树边上有多少圆点即可 还要讨论一下经不经过根\(1\)的情 ...

  5. 洛谷 P4721 【模板】分治 FFT 解题报告

    P4721 [模板]分治 FFT 题目背景 也可用多项式求逆解决. 题目描述 给定长度为 \(n−1\) 的数组 \(g[1],g[2],\dots,g[n-1]\),求 \(f[0],f[1],\d ...

  6. winform登录代码

    Program.cs文件中 static class Program { /// <summary> /// 应用程序的主入口点. /// </summary> [STAThr ...

  7. PID控制算法的C语言实现一 PID算法原理

    本系列是转载............. 全部的程序有一个共同点:就是我没认真去调pid的参数 在工业应用中PID及其衍生算法是应用最广泛的算法之一,是当之无愧的万能算法,如果能够熟练掌握PID算法的设 ...

  8. NYOJ--703

    原题链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=703 分析:先考虑不受限制的情况,此时共可以修n*(n-1)/2条隧道:所有的place组 ...

  9. kotlin 变量声明

    Kotlin 是强类型的语言,Kotlin 要求所有的变量必须先声明.后使用,声明变量时必须显示或隐式指定变量的类型(隐式的是指,声明的时候同时初始化,这样编译的时候就可以推断出该变量的类型了,Jav ...

  10. 安装好dashboard 登录出现错误

    验证发生错误.请稍后再试一次. While turning SELinux off certainly does the trick, it is somewhat like using a sled ...