hbase coprocessor 源码分析

  1. 关于HBaseCoprocessor

HBaseCoprocessor是一套通信框架,能够在客户端向RegionServer注入代码并执行获取结果。基于HBaseCoprocessor,可以实现在RegionServer层面的聚合、访问控制及二次索引等功能,从服务端丰富HBase的功能。

  1. HBase Coprocessor框架

HBaseCoprocessor的核心,是类似于HadoopHBase的轻量级RPC远程调用和通信框架。这个RPC的基本工作流程是:

1)客户端取得一个服务端通信接口的实例.

2)客户端调用这个实例中的方法

3)客户端向服务端传输调用请求

4)服务端接口实现被调用

5)服务端向客户端传输结果

Coprocessor架构中,org.apache.org.hadoop.hbase.coprocessor.CoprocessorProtocol是服务端与客户端通信的接口。

客户端

服务端

通信接口

HBase Coprocessor Client

Region Server

CoprocessorProtocol

以源码中基于CoprocessorAggregation功能实现为例,说明HBaseCoprocessor的框架。

  • Coprocessor核心接口与核心类

  • 接口Coprocessor:提供start()stop()方法,表示Coprocessor的真实执行。

  • 接口CoprocessorProtocol:提供getProtocolVersion()方法,派生接口中可以定义coprocessor需要的功能。

  • 虚拟类BaseEndPointCoprocessor:这是一个默认的EndPoint实现类,它实现了Coprocessor接口和CoprocessorProtocol接口;维护了一个CoprocessorEnvironment内部类来包装HTable和访问Region资源。

  • AggregateImplementation:这个类继承BaseEndPointCoprocessor,定义并实现了若干聚合算法,如maxminavg等方法。这个类将运行在RegionServer上,通过接口Coprocessorstart()stop()启动和停止,通过聚合算法对本Region中指定Table的数据进行聚合计算。


Coprocessor运行环境类,每一个Coprocessor都运行在Environment中。这个Environment包含了一个HTable的封装类,和一个Coprocessor的实现类;它通过实现接口CoprocessorEnvironment,提供getVersion(),getHBaseVersion, getInstance(), getPriority(), getLoadSequence(),getTable()等功能,配合Coprocessor的启动与停止,维护Coprocessor运行。

  • 接口CoprocessorEnvironment:定义方法getVersion(),getHBaseVersion, getInstance(), getPriority(), getLoadSequence(),getTable()

  • 虚拟类CoprocessorHost:为HBaseSevices启动和维护运行时的coprocessorinvocation提供一个通用框架。这个类可以载入coprocessor进行运行环境(loadInstance),查询当前的processor(getProcessors)。它为每一个coprocessor,创建一个运行Environment,并纳入管理体系。

  • 静态类CoprocessorHost.Environment:实现CoprocessorEnvironment接口,封装一个HTableCoprocessor实现类。

  • RegionCoprocessorHost:继承自CoprocessorHost,被关联至HRegion中以支持RegionServer上执行coprocessor。它在HRegion的构造函数中初始化。

  • 服务端关键类

    • HRegionHRegion在构造函数中加载RegionCoprocessorHost,即加载已配置的Coprocessor;它建立了一个Map,维护已加载(或者叫registered)的coprocessorclasshandler。其Exec()方法通过已经注册的protocolhandlers,在本地执行CoprocessorProtocol中的方法。这就可以支持客户端的RPC调用。

在初始化时,HRegion会检查加载的Coprocessor是否实现了CoprocessorProtocol接口,如果实现了该接口,则对region数据有对应的处理方法,就将其加入到registeredHandler中,加入内容为(class<T>, T)

    • HRegionServer:暴露客户端远程RPC调用的接口execCoprocessor(regionName,exec)

  • 客户端关键类

    • AggregateClient:通过Coprocessor实现的聚合client函数。通过传入AggregateProtocol类,RPC调用RegionServer上的protocolhandler,完成计算并获得返回值。它实现了minmaxavg等方法。

    • Batch:定义了RPC远程执行的接口Call,以及返回接口CallBack

    • HTable:提供RPC远程调用的coprocessorExec方法。

    • HConnectionManager:提供RPC远程调用的processExecs方法。

    • ExecRPCInvoker:通过hbaseclient中的ServerCallableRPC远程调用HRegionServer中的暴露execCoprocessor方法,并获得返回值。

  1. 客户端源码分析

client.coprocessor包中的AggragationClient为例,说明Coprocessor的执行流程
①AggregationClient
通过hadoopconf配置类初始化
②AggregationClient
实现了6个聚合方法,maxminrowcount,sum,avg,std
各个方法的实现大体一致,我们以一个典型的max为例,说明coprocessor的用法  



  1. public<R, S> R max(final byte[] tableName, finalColumnInterpreter<R, S> ci,

  2. finalScan scan) throws Throwable {

  3. validateParameters(scan);//验证参数

  4. HTabletable = new HTable(conf, tableName); //创建htable

  5. classMaxCallBack implements Batch.Callback<R> {//定义callback函数,将运行在regionserver

  6. Rmax = null;

  7. RgetMax() {

  8. returnmax;

  9. }

  10. @Override

  11. publicsynchronized void update(byte[] region, byte[] row, R result) {

  12. //columnintepretercompare,比较两个对象的大小,返回正数或者负数值

  13. //columnintepreter,根据T值,作计算。<T,S>,TcellvalueSpromoteddata type

  14. max= ci.compare(max, result) < 0 ? result : max;//重写update函数,获得columnfamily中的最大值

  15. }

  16. }

  17. MaxCallBackaMaxCallBack = new MaxCallBack();//创建一个callback对象

  18. //通过htable中增加新的coprocessorExec方法,实现callcallback

  19. //必须实现coprocessorProtocol,这里是AggregateProtocol接口,继承了coprocessorProtocol,定义好了聚合的各种方法

  20. table.coprocessorExec(AggregateProtocol.class,scan.getStartRow(), scan//执行callback,核心类是AggregateProtocol

  21. .getStopRow(),

  22. newBatch.Call<AggregateProtocol, R>() {//callregionserver上执行的事务

  23. @Override

  24. publicR call(AggregateProtocol instance) throws IOException {

  25. returninstance.getMax(ci, scan); //get max

  26. }

  27. },

  28. aMaxCallBack);//传入back对象

  29. returnaMaxCallBack.getMax();

  30. }   



以上方法中的传入参数

-- final byte []tableName:查询的表名称

-- finalColumnIntepreter<R,S> ci:对列值的解释器和计算器,以进行列值计算和比较

-- final Scanscan:定义查询范围,单列值或是多列值,有无起始的rowkey范围,有无timerange



以上示例中,可以看出,

1.AggregationClientAggregateProtocol接口类(继承至CoprocessorProtocol)为通信接口,传递给RPC调用方法,Region上获得此AggregateProtocol,在本地调用Handler进行处理,将处理完毕的值返回。看起来,远程调用就像本地执行一样。

2.需要定义region上执行的类Batch.Call,即newBatch.Call<AggregateProtocol, R>()

3.需要定义回调函数Batch.CallBack的实现类,即MaxCallBack

4. 通过client中的HTablecoprocessorExec方法,执行coprocessor,并获得返回值。

  1. RPC调用源码分析

  1. org.apache.hadoop.hbase.client.coprocessor.Batch虚拟类,定义了与Coprocessor进行RPC交互的方法和接口。

  --静态接口Call<T,R>T是调用的实例,R是返回值类型。定义了call方法,即针对实例T执行的方法。

  --静态接口CallBack<R>R是返回值类型,定义了update方法;即通过RPC调用获得Region上的coprocessor计算结果后,和现有值进行计算更新。

  --静态方法Call<T,R> forMethod(finalClass<T> protocol, final String method, final Object...args):通过protocol、方法名、参数,返回Call接口的实现类引用 

org.apache.hadoop.hbase.client.HConnectionManager.processExec()方法

  --由代理类Proxy生成protocol类,并实现相应接口,由invoker调用接口方法。R是计算结果,regionNameinvoker获得,callback更新计算值。

  -- ExecRPCInvoker-> Exec为参数,HRegionServer(继承HRegionInterface)execCoprocessor函数-> HRegion.exec()

  --HRegion.registerProtocol(Class<T> protocol, T handler),注册Region上的protocolHandler;该方法被RegionCoprocessorHost构造函数初始化loadSystemCoprocessors时调用(createEnvironment)



HConnectionManager.processExec()方法的核心代码



  1. for(final byte[] r : rows) {//针对每一个range的起始行,调用invoke函数

  2. finalExecRPCInvoker invoker =

  3. newExecRPCInvoker(conf, this, protocol, tableName, r);   //建立一个rpc通道

  4. Future<R>future = pool.submit(//提交任务,获得future

  5. newCallable<R>() {

  6. publicR call() throws Exception {

  7. Tinstance = (T)Proxy.newProxyInstance(conf.getClassLoader(),

  8. newClass[]{protocol},

  9. invoker);      //创建一个coprocessorProtocol接口的本地proxy,以远程调用region上的coprocessor,获得返回值

  10. Rresult = callable.call(instance);      //通过rpc通道,proxyinvoker远程调用call指定的方法(minmax等),传递返回值给R

  11. byte[]region = invoker.getRegionName();  //invoker.call方法已经为invokerregionName赋值;这个方法直接返回regionName

  12. if(callback != null) {

  13. callback.update(region,r, result);      //callbackupdate方法,对rpc计算获得值和原有值作聚合计算(minmax等),更新结果值

  14. }

  15. returnresult;

  16. }

  17. });

  18. futures.put(r,future);

  19. }



ExecRPCInvokerinvoke核心代码



  1. publicObject invoke(Object instance, final Method method, final Object[]args)

  2. throwsThrowable {

  3. if(LOG.isDebugEnabled()) {

  4. LOG.debug("Call:"+method.getName()+", "+(args != null ?args.length : 0));

  5. }

  6. if(row != null) {

  7. finalExec exec = new Exec(conf, row, protocol, method, args);

  8. ServerCallable<ExecResult>callable =

  9. newServerCallable<ExecResult>(connection, table, row) {

  10. publicExecResult call() throws Exception {

  11. returnserver.execCoprocessor(location.getRegionInfo().getRegionName(),

  12. exec);    //通过RPC,调用HRegionServer上暴露的方法

  13. }

  14. };

  15. ExecResultresult = connection.getRegionServerWithRetries(callable);

  16. this.regionName= result.getRegionName();

  17. LOG.debug("Resultis region="+ Bytes.toStringBinary(regionName) +

  18. ",value="+result.getValue());

  19. returnresult.getValue();

  20. }



HRegionexec核心代码



  1. CoprocessorProtocolhandler = protocolHandlers.getInstance(protocol);   //protocolHandlersRegionCoprocessorHost初始化时载入,即预设的coprocessor集合

  2. Objectvalue;

  3. Class<?>returnType;

  4. try{

  5. Methodmethod = protocol.getMethod(

  6. call.getMethodName(),call.getParameterClasses());

  7. method.setAccessible(true);

  8. returnType= method.getReturnType();

  9. value= method.invoke(handler, call.getParameters());     //HRegion上的本地接口执行方法,这是实际执行的地方


  10. 后续重要的是coprocessor对hlog的分析


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值