HBase学习 - Coprocessor

本文基于hbase-1.3.0源码

1. 前言

HBase coprocessor(协处理器)按照工作方式分为两种:endpoint和observer。

  1. endpoint
    类似mysql数据库中的存储过程,用户继承接口编写endpoint,由hbase加载。加载完成后可以由客户端rpc调用。
  2. observer
    observer类似mysql中的触发器,同样由用户继承接口编写observer,由hbase加载。observer还可以细分为4类,下文会细讲。observer不需要客户端通过rpc去调用,observer满足条件时触发。

由于这两类之间没有什么关联,下文将按照这两类分章节讲述。

2. Observer

2.1 observer分类

observer可以划分为四类,通过实现下面4个接口来自定义4类obserser:

  1. RegionServerObserver
    此类observer应该由region server加载,并在region server上发生操作是触发
  2. RegionObserver
    此类observer应该由region创建时加载,并在region上发生操作时触发
  3. MasterObserver
    此类observer应该由master创建时加载,在master上发生操作时触发
  4. WALObserver
    此类observer是在创建wal(write ahead log)时加载,在wal发生相关操作时触发。

以上4类接口定义的一些接口方法,实际上作为一种钩子方法,会在涉及regionserver/region/master/wal不同操作时由hbase调用。比如RegionObserver中preAppendpostAppend这种会在对region做append操作完成前后回调 。自定义observer实现这些接口方法,由加载observer的执行环境根据当前操作决定调用observer的哪种方法。

由于每一类observer都可以定义任意个数,理论上每一个observer都会触发。

2.2 执行环境

上文提到不同的observer会在不同的地方加载。针对每一类observer,Hbase都会创建一个叫 XXXCoprocessorHost的实例,名字带Host说明它是提供加载和运行环境的,它主要负责以下工作:

  1. 负责加载并管理相应类型的observer,比如RegionCoprocessorHost只加载所有的RegionObserver。
  2. 并在特定操作发生时,hbase是通过对应的XXXCoprocessorHost触发其加载的所有observer的对应方法,。
    比如region上发生append操作时,调用RegionCoprocessorHost # preAppend 在append之前触发所有加载的RegionObserver 的preAppend方法。

和4种observer中类对应的 XXXCoprocessorHost同样分为4类:

  • RegionCoprocessorHost
  • RegionServerCoprocessorHost
  • MasterCoprocessorHost
  • WALCoprocessorHost

下面先说说这四类CoprocessorHost吧。
由于它们都继承自Coprocessor这个抽象类,需要先解释以下这个类:
1. CoprocessorHost
这是这个类的声明:

public abstract class CoprocessorHost 
{...}
-------------------
他有如下成员:
1. protected SortedList coprocessors =
      new SortedList(new EnvironmentPriorityComparator());
   coprocessor保存了当前Coprocessor,类型参数E 是CoprocessorEnvironment的子类,这里是对Coprocessor做了以下封装,
    CoprocessorEnvionment中还保存了一些Coprocessor执行的必要环境,后文会讲到。

2. protected void loadSystemCoprocessors(Configuration conf, String confKey) 
    XXXCoprocessorHost创建时,会根据配置文件默认加载的coprocessor,由配置文件中的confKey这个配置项决定,不同的CoprocessorHost这个confKey时不同的,讲到CoprocessorHost时会提到。

3. public E load(Path path, String className, int priority,
      Configuration conf)
   这个方法是用来从hdfs上加载类的,path是hdfs上存放的coprocessor所在的jar包, className即要加载的类。
4. public abstract E createEnvironment(Class implClass, Coprocessor instance,
      int priority, int sequence, Configuration conf)
   这是抽象方法,具体的XXXCoprocessorHost需要实现, load加载并创建出coprocessor实例,委托这个方法将coprocessor包装到特定的enviroment中。

2. Environment
用户只需要要着眼于实现Coprocessor,但是XXXCoprocessorHost加载coprocessor后会包装成Environment,它是一个公共基类,XXXCoprocessorHost中都会继承它实现一个特定的enviornment,并在里面包装一些自己的信息。

提供Environment的意义在于:

有时后需要在自定义的coprocessor中访问hbase中的table或者访问hmaster或者hregionserver的一些服务,访问region信息等等,Enviornment中提供了这个便利。

enviroment还提供了在不同同一种CoprocessorHost加载的不同coprocessor中共享数据的方式,也就是通过Enviroment

每一个coprocessor方法的第一个参数都如同下面那样:

  void xxx(final ObserverContext c, ...) throws IOException;

CoprocessorHost在调用coprocessor方法时,会把自己的enviornment注入到这个参数上,自定义的coprocessor可以调用。

不同的CoprocessorHost会使用不同的Enviornment,如下:

  • RegionEnvironment: RegionCoprocessorHost创建
  • MasterEnviornment:MasterCoprocessorHost创建
  • RegionServerEnvionment: RegionServerCoprocessorHost创建
  • WALEnviornment: WALCoprocessorHost创建

2.2.1 RegionCoprocessorHost

RegionCoprocessorHost在创建HRegion时创建,它管理所有RegionObserver的实现类,并调用它们的钩子方法。
1. 加载的Coprocessor

  • 由配置hbase.coprocessor.region.classes加载指定的coprocessor;
  • 此外,如果当前Region上的表为非系统表(即不是'hbase'这个namespace下的表),那么它还会加载配置hbase.coprocessor.user.region.classes对应的class。
  • 此外,还会加载通过表属性字段设置的一些coprocessor,属性名必须是:coprocessor-{数字}(说明可以是多个coprocessor), 属性值符合:"url_to_your_jar|coprocessor_classname|priority_no|arg1=value1,arg2=value2,...,argN=valueN"这种格式,下面是一个hbase中的例子:
    hdfs:///foo.jar|com.foo.FooRegionObserver|1001|arg1=1,arg2=2
    jar包在hdfs上,加载coprocessor为“ com.foo.FooRegionObserver”,优先级1001(越低越优先调用), 后面的参数被设置到Configuration中,Configuration又被封装到Environment中。
    

附:
RegionCoprocessorHost还会加载Endpoint Service,如果你的Coprocessor实现了接口CoprocessorService,实现了接口的唯一方法Service getService();。那么还会注册这个service到region上,然后就可以通过rpc去调用这个endpoint service。这个会在后面Endpoint一节中讲述。
2. coprocessor的执行
这里从源码介绍一下RegionCoprocessorHost是怎么执行Coprocessor的。coprocessor的钩子方法是通过RegionCoprocessorHost的一些对等的方法调用的,以RegionCoprocessorHost的下面两个方法为例:

class RegionCoprocessorHost{
...
 public InternalScanner preFlush(final Store store, final InternalScanner scanner)
      throws IOException {
    return execOperationWithResult(false, scanner,
        coprocessors.isEmpty() ? null : new RegionOperationWithResult() {
      @Override
      public void call(RegionObserver oserver, ObserverContext ctx)
          throws IOException {
        setResult(oserver.preFlush(ctx, store, getResult()));
      }
    });
  }

  /**
   * Invoked before a memstore flush
   * @throws IOException
   */
  public void preFlush() throws IOException {
    execOperation(coprocessors.isEmpty() ? null : new RegionOperation() {
      @Override
      public void call(RegionObserver oserver, ObserverContext ctx)
          throws IOException {
        oserver.preFlush(ctx);
      }
    });
  }
...
}
----------------
1. 这两个方法都是在Memstore flush到磁盘前调用的,这两个方法会调用RegionCoprocessorHost加载的所有的RegionCoprocessor # preFlush方法。

2. 有个重要的区别是前者有返回值,后者没有。有返回值的Coprocessor方法意味着可以用返回值替换输入,比如上面第一个方法输入参数scanner可以被替换为返回值,编写coprocessor的人应该很清楚自己在干什么。

3. 这么多coprocessor,是按什么顺序执行的?按优先级,Environment 创建时都会指定优先级,通过getPriority()返回.
上面「1. 加载的Coprocessor」中那个表属性设置加载的coprocessor中的例子有优先级‘1001’,数字越小,优先级越高。系统加载的优先级都比较低(Integer.MAX_VALUE / 4)。

由于其他几类CoprocessorHost执行coprocessor的方式差不多,后面就不再介绍了。
3. RegionEnviornment
所有的RegionObserver的方法第一个参数都是ObserverContext的子类,从上面的代码可以看出这个context是RegionCoprocessorHost注入的,ObserverContext主要的成员就是Environment的对象,各类CoprocessorHost都实现了Environment,在里面包装了不同的信息供coprocessor使用。
下面看看可以使用RegionEnviornement干什么:

RegionEnviornment的主要成员:
1.  private Region region;
    加载当前RegionCoprocessorHost的Region
2. private RegionServerServices rsServices;
    用于访问RegionServer
3. ConcurrentMap sharedData;
    sharedData用来在同一个coprocessor的多次调用之间共享数据
主要方法:
1.  public Configuration getConfiguration() 
   获取hbase配置信息
2. HTableInterface getTable(TableName tableName)
   从表名获取访问以及操作表的接口。

2.2.2 RegionServerCoprocessorHost

它在RegionServer启动过程中创建,因此一个RegionServer只有一个。RegionCoprocessorHost都是涉及到当前的region的一些操作时(put,get,split等)会回调,RegionServerCoprocessorHost显然涉及的操作都是RegionServer范围可见的(比如多个region的merge,基于wal的一些操作,wal时region server范围内维持的,等等)。
1. 加载的coprocessor
加载由配置项hbase.coprocessor.regionserver.classes决定的coprocessor。要想coprocessor在上运行,需要实现接口RegionServerObserver

附:
如果你的coprocessor实现了SingletonCoprocessorService接口,那么RegionServerCoprocessorHost会调用它的唯一接口方法‘getService()’返回一个endpoint service,并注册这个endpoint,然后就可以通过客户端调用了。
2. RegionServerEnvironment
比较简单,只有一个成员如下:

private RegionServerServices regionServerServices;
访问region server的一些服务

2.2.3 MasterCoprocessorHost

在HMaster启动过程中创建, master负责的操作很多(修改表,region 相关操作,procedure相关的)。
1. 加载的coprocessor
由配置项hbase.coprocessor.master.classes决定, MasterCoprocessorHost上运行的coprocessor需要实现‘MasterObserver’接口。
附:
如果coprocessor实现了接口CoprocessorService,那么MasterCoprocessorHost会调用这个接口的唯一方法"getService"获得endpoint,然后注册它。
2. MasterEnvironment

成员:
private MasterServices masterServices;
访问master的一些服务

2.2.4 WALCoprocessorHost

2.2.5 自定义Observer

3 Endpoint

你可能感兴趣的:(HBase学习 - Coprocessor)