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<E extends CoprocessorEnvironment> 
{...}
-------------------
他有如下成员:
1. protected SortedList<E> coprocessors =
      new SortedList<E>(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<XXXCoprocessorEnvironment> 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<InternalScanner>() {
      @Override
      public void call(RegionObserver oserver, ObserverContext<RegionCoprocessorEnvironment> 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<RegionCoprocessorEnvironment> 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<String, Object> 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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容