HBase协处理器及实例

为什么引入协处理器?

HBase作为列数据库最经常被人诟病的特性包括:无法轻易建立“二级索引”,难以执行求和、计数、排序等操作。

比如,在旧版本(<0.92)的Hbase中,统计数据表的总行数,需要使用Counter方法,执行一次MapReduce Job才能得到。

虽然HBase在数据存储层中集成了MapReduce,能够有效用于数据表的分布式计算。然而在很多情况下,做一些简单的相加或者聚合计算的时候,如果直接将计算过程放置在server端,能够减少通讯开销,从而获得很好的性能提升。于是, HBase在0.92之后引入了协处理器(coprocessors)。

协处理器实现了一些激动人心的新特性:能够轻易建立二次索引、复杂过滤器(谓词下推)以及访问控制等。

灵感来源

HBase协处理器的灵感来自于Jeff Dean 09年的演讲。它根据该演讲实现了类似于bigtable的协处理器,包括以下特性:

1、每个表服务器的任意子表都可以运行代码。

2、客户端的高层调用接口 (客户端能够直接访问数据表的行地址,多行读写会自动分片成多个并行的RPC调用)。

3、提供一个非常灵活的、可用于建立分布式服务的数据模型。

4。能够自动化扩展、负载均衡、应用请求路由

HBase的协处理器灵感来自 bigtable,但是实现细节不尽相同。 HBase建立了一个框架,它为用户提供类库和运行时环境,使得他们的代码能够在HBase region server和master上处理。

协处理器类型

协处理器分两种类型:

系统协处理器:可以全局导入region server上的所有数据表。

表协处理器:用户可以指定一张表使用协处理器。

协处理器插件

协处理器框架为了更好支持其行为的灵活性,提供了两个不同方面的插件。

一个是观察者(observer),类似于关系数据库的触发器。

另一个是终端(endpoint),动态的终端有点像存储过程。

Observer

观察者的设计意图是允许用户通过插入代码来重载协处理器框架的upcall方法,而具体的事件触发的callback方法由HBase的核心代码来执行。

协处理器框架处理所有的callback调用细节,协处理器自身只需要插入添加或者改变的功能。

以HBase0.92版本为例,它提供了三种观察者接口

RegionObserver:提供客户端的数据操纵事件钩子: Get、 Put、 Delete、Scan等。

WALObserver:提供WAL相关操作钩子。

MasterObserver:提供DDL-类型的操作钩子。如创建、删除、修改数据表等。

这些接口可以同时使用在同一个地方,按照不同优先级顺序执行。用户可以任意基于协处理器实现复杂的HBase功能层。 HBase有很多种事件可以触发观察者方法,这些事件与方法从HBase0.92版本起,都会集成在HBase API中。不过这些API可能会由于各种原因有所改动,不同版本的接口改动比较大。

RegionObserver工作原理,如图1所示。

EndPoint

终端是动态RPC插件的接口,它的实现代码被安装在服务器端,从而能够通过HBase RPC唤醒。客户端类库提供了非常方便的方法来调用这些动态接口,它们可以在任意时候调用一个终端,它们的实现代码会被目标region远程执行,结果会返回到终端。用户可以结合使用这些强大的插件接口,为HBase添加全新的特性。

终端的使用如下面流程所示:

定义一个新的protocol接口,必须继承CoprocessorProtocol。

实现终端接口,该实现会被导入region环境执行。

继承抽象类BaseEndpointCoprocessor。

在客户端,终端可以被两个新的HBase Client API调用。

单个region:

HTableInterface.coprocessorProxy(Class<T> protocol, byte[] row) 

regions区域:

HTableInterface.coprocessorExec(Class<T> protocol, byte[] startKey, byte[] endKey, Batch.Call<T,R> callable)

整体的EndPoint调用过程范例,如图所示:

官方给的示例程序大体流程:

对Endpoint进行设置的三个方法:

A. 启动全局aggregation,能过操纵所有的表上的数据。通过修改hbase-site.xml这个文件来实现,只需要添加如下代码:

<property>
    <name>hbase.coprocessor.user.region.classes</name>
    <value>
        org.apache.hadoop.hbase.coprocessor.RowCountEndpoint
    </value>
</property>

B. 启用表aggregation,只对特定的表生效。通过HBase Shell 来实现。

(1)disable指定表。

hbase> disable ‘mytable‘    

(2)添加aggregation 。

hbase> alter ‘mytable‘,‘coprocessor‘=>‘|org.apache.hadoop.hbase.coprocessor.example.RowCountEndpoint ||‘

(3)重启指定表 。

hbase> enable ‘mytable‘

C. API调用

HTableDescriptor htd=new HTableDescriptor("testTable");
htd.setValue("CORPROCESSOR$1" ,
path.toString+"|"+RowCountEndpoint.class.getCanonicalName()+"|"+Coprocessor.Priority.USER);

其中path为jar在HDFS中的路径。

Demo1:

package Coprocessor;

import java.io.IOException;
import java.util.Iterator;
import java.util.Map;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.client.HConnection;
import org.apache.hadoop.hbase.client.HConnectionManager;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.coprocessor.Batch;
import org.apache.hadoop.hbase.coprocessor.example.generated.ExampleProtos;
import org.apache.hadoop.hbase.ipc.BlockingRpcCallback;
import org.apache.hadoop.hbase.ipc.ServerRpcController;

import com.google.protobuf.ServiceException;

public class CoprocessorRowCounter
{

    private String rootDir;
    private String zkServer;
    private String port;
    private Configuration conf;
    private HConnection hConn = null;

    private CoprocessorRowCounter(String rootDir,String zkServer,String port) throws IOException{
        this.rootDir = rootDir;
        this.zkServer = zkServer;
        this.port = port;

        conf = HBaseConfiguration.create();
        conf.set("hbase.rootdir", rootDir);
        conf.set("hbase.zookeeper.quorum", zkServer);
        conf.set("hbase.zookeeper.property.clientPort", port);

        hConn = HConnectionManager.createConnection(conf);
    }

    public static void main(String[] args) throws ServiceException, Throwable
    {

        String rootDir = "hdfs://hadoop1:8020/hbase";
        String zkServer = "hadoop1";
        String port = "2181";

        CoprocessorRowCounter conn = new CoprocessorRowCounter(rootDir,zkServer,port);

        //Configuration conf = HBaseConfiguration.create();

        HTable table = new HTable(conn.conf, "students");
        //发送请求
        final ExampleProtos.CountRequest request = ExampleProtos.CountRequest.getDefaultInstance();

        //回调函数 call方法
        Map<byte[],Long> results = table.coprocessorService(ExampleProtos.RowCountService.class,
            null, null,
            new Batch.Call<ExampleProtos.RowCountService,Long>()
        {

            public Long call(ExampleProtos.RowCountService counter) throws IOException
            {
                ServerRpcController controller = new ServerRpcController();

                BlockingRpcCallback<ExampleProtos.CountResponse> rpcCallback =
                    new BlockingRpcCallback<ExampleProtos.CountResponse>();

                //实现在server端
                counter.getRowCount(controller, request, rpcCallback);

                ExampleProtos.CountResponse response = rpcCallback.get();

                if (controller.failedOnException()) {
                  throw controller.getFailedOn();
                }
                //返回
                return (response != null && response.hasCount()) ? response.getCount() : 0;
            }
        });

        int sum = 0;
        int count = 0;
       /* Iterator<Long> iter = results.values().iterator();
        Long val = iter.next();*/
        for (Long  l : results.values()) {
            sum += l;
            count++;
        }
        System.out.println("row count = " + sum);
        System.out.println("region count = " + count);

    }
}

运行结果:

row count = 4
region count = 1

注意:

  1. 协处理器配置的加载顺序:先加载配置文件中定义的协处理器,后加载表描述符中的协处理器。
  2. COPROCESSOR$<number>中的number定义了加载的顺序。
  3. 协处理器配置格式

移除协处理器

Demo2:

package Coprocessor;

import java.io.IOException;
import java.util.List;

import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.coprocessor.BaseRegionObserver;
import org.apache.hadoop.hbase.coprocessor.ObserverContext;
import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
import org.apache.hadoop.hbase.util.Bytes;

public class RegionObserver extends BaseRegionObserver{

    private static byte[] fixed_rowkey = Bytes.toBytes("Ivy");

    //preGetOp代替preGet
    /*public void preGetOp(ObserverContext<RegionCoprocessorEnvironment> e,
            Get get, List<Cell> results) throws IOException {
        if (Bytes.equals(get.getRow(), fixed_rowkey)) {
            //行键  列族 列
            Cell cell = new KeyValue(get.getRow(), Bytes.toBytes("time"),
                    Bytes.toBytes("time"));
            results.add(cell);
        }
    }*/

    @Override
    public void preGet(ObserverContext<RegionCoprocessorEnvironment> c,
            Get get, List<KeyValue> result) throws IOException {
        if (Bytes.equals(get.getRow(), fixed_rowkey))
        {

            //行键  列族 列
            KeyValue kv = new KeyValue(get.getRow(), Bytes.toBytes("time"),
                        Bytes.toBytes("time"),Bytes.toBytes(System.currentTimeMillis()));
            result.add(kv);
        }
    }
}

打成jar包,上传到hdfs上面。

增加协处理器

disable ‘students‘
alter ‘students‘,‘coprocessor‘=>‘hdfs://nameservice1/liguodong/coprocessor.jar|Coprocessor.RegionObserver||‘
enable ‘students‘

package HbaseAPI;

import java.io.IOException;
import java.util.List;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.MasterNotRunningException;
import org.apache.hadoop.hbase.ZooKeeperConnectionException;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.HBaseAdmin;
import org.apache.hadoop.hbase.client.HConnection;
import org.apache.hadoop.hbase.client.HConnectionManager;
import org.apache.hadoop.hbase.client.HTableInterface;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.io.compress.Compression.Algorithm;
import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding;
import org.apache.hadoop.hbase.util.Bytes;

public class HBaseConnection {
    private String rootDir;
    private String zkServer;
    private String port;
    private Configuration conf;
    private HConnection hConn = null;

    private HBaseConnection(String rootDir,String zkServer,String port) throws IOException{
        this.rootDir = rootDir;
        this.zkServer = zkServer;
        this.port = port;

        conf = HBaseConfiguration.create();
        conf.set("hbase.rootdir", rootDir);
        conf.set("hbase.zookeeper.quorum", zkServer);
        conf.set("hbase.zookeeper.property.clientPort", port);

        hConn = HConnectionManager.createConnection(conf);
    }

    //获取数据
    public Result getData(String tableName,String rowkey) throws IOException{

        HTableInterface table = hConn.getTable(tableName);

        //用来获取单个行的相关信息
        Get get = new Get(Bytes.toBytes(rowkey));

        return table.get(get);
    }

    public void format(Result result){
        //行键
        String rowkey = Bytes.toString(result.getRow());

        //Return an cells of a Result as an array of KeyValues
        KeyValue[] kvs = result.raw();

        for (KeyValue kv : kvs) {
            //列族名
            String family = Bytes.toString(kv.getFamily());
            //列名
            String qualifier = Bytes.toString(kv.getQualifier());

            //String value = Bytes.toString(result.getValue(Bytes.toBytes(family), Bytes.toBytes(qualifier)));
            String value = Bytes.toString(kv.getValue());

            System.out.println("rowkey->"+rowkey+", family->"
            +family+", qualifier->"+qualifier);
            System.out.println("value->"+value);
        }
    }

    public static void main(String[] args) throws IOException {
        String rootDir = "hdfs://hadoop1:8020/hbase";
        String zkServer = "hadoop1";
        String port = "2181";
        //初始化
        HBaseConnection conn = new HBaseConnection(rootDir,zkServer,port);

        //输出结果
        Result result = conn.getData("students", "Ivy");
        conn.format(result);

        result = conn.getData("students", "Tom");
        conn.format(result);
    }
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-15 15:33:54

HBase协处理器及实例的相关文章

hbase协处理器编码实例

Observer协处理器通常在一个特定的事件(诸如Get或Put)之前或之后发生,相当于RDBMS中的触发器.Endpoint协处理器则类似于RDBMS中的存储过程,因为它可以让你在RegionServer上对数据执行自定义计算,而不是在客户端上执行计算. 本文是以上两者的简单实例,使用的环境:环境 jdk1.8 hadoop2.6.5 hbase1.2.4. 1.Endpoint实例  1> 编写适用于protobuf的proto文件,如下,尽量不要带注释,因为编译时可能出现乱码 option

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

1.简介 不同于Observer协处理器,EndPoint由于需要同region进行rpc服务的通信,以及客户端出数据的归并,需要自行实现客户端代码. 基于[How to] 使用HBase协处理器---Endpoint服务端的实现这篇文章,我们继续实现其客户端代码. 2.客户端代码实现方式介绍 目前基于HBase1.0.0的版本客户端一共可以基于以下五个API来实现: 1. Table.coprocessorService(byte[]) 基于单个region的服务请求,参数为rowKey值,被

HBase协处理器

说明:类似于RDBMS中触发器,允许用户在region服务器上运行自己的代码,在客户端用户不用关心操作具体在哪进行 使用场景:权限控制,回调函数(钩子函数).扫描统计等 主要类:observer和endpoint observer:类似触发器,回调函数在特定事件发生时执行 endpoint:类似数据库存储过程, Coprocessor:定义了协处理器的基本约定,所有协处理器必须实现Coprocessor类 协处理器加载:可以在hbase-site.xml中配置或者通过表描述符加载 eg:使用配置

HBase协处理器实战

主要内容: 1. HBase协处理器介绍 2. 观察者(Observer) 3. 终端(endpoint) ------------------------------------------------------------------------------------------------------------------------------------------------------------------- 1. HBase协处理器介绍 系统协处理器可以全局导入regio

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

一. 背景二. 什么是HBase的协处理器三. HBase协处理器同步数据到Solr四. 添加协处理器五. 测试六. 协处理器动态加载 一. 背景 在实际生产中,HBase往往不能满足多维度分析,我们能想到的办法就是通过创建HBase数据的二级索引来快速获取rowkey,从而得到想要的数据.目前比较流行的二级索引解决方案有Lily HBase Indexer,Phoenix自带的二级索引,华为Indexer,以及360的二级索引方案.上面的目前使用比较广泛的应该是Lily HBase Index

hbase 协处理器

一.服务端1.安装Protobuf2.RPC proto 定义文件:Examples.protooption java_package = "org.apache.hadoop.hbase.coprocessor.example.generated";option java_outer_classname = "ExampleProtos";option java_generic_services = true;option java_generate_equals

HBase 协处理器统计行数

环境:cdh5.1.0 启用协处理器方法1. 启用协处理器 Aggregation(Enable Coprocessor Aggregation) 我们有两个方法:1.启动全局aggregation,能过操纵所有的表上的数据.通过修改hbase-site.xml这个文件来实现,只需要添加如下代码: <property> <name>hbase.coprocessor.user.region.classes</name> <value>org.apache.h

Hbase协处理器coprocessor

分为两种类型 observer 观察者相当于触发器 Endpoint终端相当于存储过程 下面的观察者实现查询之前替换掉行键为Jack的KeyValue import java.io.IOException; import java.util.List; import org.apache.hadoop.hbase.KeyValue; import org.apache.hadoop.hbase.client.Get; import org.apache.hadoop.hbase.coproces

HBase 协处理器详解

一.简述 在使用HBase时,如果你的数据量达到了数十亿行或数百万列,此时能否在查询中返回大量数据将受制于网络的带宽,即便网络状况允许,但是客户端的计算处理也未必能够满足要求.在这种情况下,协处理器(Coprocessors)应运而生.它允许你将业务计算代码放入在RegionServer的协处理器中,将处理好的数据再返回给客户端,这可以极大地降低需要传输的数据量,从而获得性能上的提升.同时协处理器也允许用户扩展实现HBase目前所不具备的功能,如权限校验.二级索引.完整性约束等. 二.协处理器类