HBASE-客户端架构

简介

HBASE的官方客户端是全异步的形式做的,底层采用的netty框架,上层接口分为两类,一类是同步接口,一类是异步接口,同步接口是对异步接口的封装。
下面,我们按照剥洋葱源码阅读法,看客户端的架构。
第一步,什么是剥洋葱源码阅读法:
一般开源项目的源码组织都是非常不错的,层次分明,就像洋葱一样,我们阅读源码,其实就是看他的架构是什么样的,就像剥洋葱一样,层层剥开他的封装。
我们用.代表接口的继承,用-代表类内部依赖的其他的类。

洋葱的第一层皮:顶层接口

Connection:代表客户端向HBase集群的一个连接。
AsyncConnection:代表客户端向HBase的一个链接,方法为异步操作。
Table:代表HBASE中的一张表。
AsyncTable:代表异步操作的一张表。
Admin:代表客户端控制台。
AsyncAdmin:代表异步操作的客户端控制台。
以上六个接口,从归类上来说,其实是三个,Connection,Table,Admin,这三个接口之间的关系,是Connection作为底层支撑组件而存在,他负责与底层的通讯,而Table与Admin这两类操作接口,是借助Connection接口的功能而实现的。

注:异步接口,如果是不需要返回值的类型,会返回一个Future对象,以让上层线程能够获得任务执行的状态,如果有返回值,则会需要传入一个Consumer对象,用于异步处理返回值。
单纯看异步来说,如果是没有返回值的方法,也是有必要传入Consumer作为参数,用以操作完成后做一些收尾工作。

使用代码:

        Configuration conf = HBaseConfiguration.create();
        conf.set(HConstants.ZOOKEEPER_QUORUM,"xxx.xxx.xxx.xxx");
        Connection connection = ConnectionFactory.createConnection(conf);

我们先从Connection操作接口说开去。

洋葱的第一层皮-1:Connection

Connection接口代表着和集群的一个连接,提供同步操作接口,也包含着集群所有的元数据信息,其实现类为ConnectionOverAsyncConnection,而这个类从名字上也能看出来,是对AsyncConnection的异步转同步的操作的封装。

洋葱的第一层皮-2 AsyncConnection

AsyncConnection接口代表着和集群的一个连接,提供异步操作的接口,主要实现类为AsyncConnectionImpl。
AsyncConnectionImpl 提供了以下几方面的内容

  1. 集群配置信心的查询:Configuration
  2. 集群Region信息的查询:AsyncRegionLocator
  3. 与集群各个server的连接的缓存(RpcClient)
  4. 与后端server进行交互时需要的组件的工厂(RpcControllerFactory,AsyncRpcRetryingCallerFactory)
    这些功能在下面的Table接口的实现中都会用到。

洋葱的第2.1-1层皮:Configuration

AsyncConnection.AsyncConnectionImpl-Configuration
这个类是配置客户端的类,从上面的示例代码上来看,必须的配置为hbase zookeeper的地址。

洋葱的第2.1-2层皮:AsyncRegionLocator

AsyncConnection.AsyncConnectionImpl-AsyncRegionLocator
这个类主要的功能类是AsyncMetaRegionLocato和AsyncNonMetaRegionLocator,这两个类分别用于定位meta region的位置和no meta region的位置。
meta region的位置是放在了zk或者其他存储介质上的,而no meta region的位置放在了meta region上面,这也就是说,如果要查询no meta region的位置,要先查询meta的位置。
其余还有一个辅助类,名字叫HashedWheelTimer,这个类的主要作用为处理超时,当获得region的请求时间超时时,这个类负责以异常的形式结束现有请求。

洋葱的第2.1-2-1层皮:AsyncMetaRegionLocator

AsyncConnection.AsyncConnectionImpl-AsyncRegionLocator-AsyncMetaRegionLocator
这个类的主要实现是委托给了AsyncRegistry,其主要实现类是ZKAsyncRegistry,因为hbase的配置中心使用的是zk,当然,如果你喜欢的话,也可以给封一层etcd的。
剩余的两个AtomicReference是做的缓存,避免客户端频繁读取zk把zk给读挂了。

洋葱的第2.1-2-1-1层皮:ZKAsyncRegistry

这个类就简单了,其实是读取zk上面的配置,拿到手后返回给上一层。

洋葱的第2.1-2-2层皮:AsyncNonMetaRegionLocator

这个类是从meta表中检索出table的region location的类,使用的是客户端的scan操作,这里面会有一个循环依赖的问题,即当你从上层检索一个表中的内容的时候,会先使用同样的上层sacn操作检索meta表,这就导致了循环依赖问题的产生。

洋葱的第2.1-3层皮:RpcClient

RpcClient是一个接口,他的作用像一个工厂,用来生成BlockingRpcChannel和RpcChannel(非Blocking)。
BlockingRpcChannel和RpcChannel是proto的rpc框架与后端的通信接口(为什么必须要兼容proto的rpc接口啊,感觉好鸡肋,自己写更灵活)。
RpcClient的主要实现类为NettyRpcClient,在这中间,还有一个AbstractRpcClient。
AbstractRpcClient主要负责发送数据的准备工作,并缓存RpcConnection对象,剩余的工作交付给RpcConnection去做,而如何获得RpcConnection,是委托给子类NettyRpcClient实现。

洋葱的第2-3-1层皮:Codec,CompressionCodec

这些是用于生成RpcConnection的,用途是将生成的字节流进行编码和压缩。

洋葱的第2-3-1层皮:PoolMap<ConnectionId, T>

这个map是用于缓存RpcConneciton的,防止生成多次对server端的连结。

洋葱的第一层皮-3:Table

Table为Hbase同步操作table的接口,实现类为TableOverAsyncTable,它是基于异步AsyncTable实现的,上面说到,整个Hbase的官方客户端为全异步架构,Table只是封装了一层底层的AsyncTable罢了。

洋葱的第一层皮-4:AsyncTable

AsyncTable的实现类有两个。

  1. AsyncTableImpl
  2. RawAsyncTableImpl

洋葱的第4.1层皮:AsyncTableImpl

AsyncTableImpl这个类是为了对scan的result做异步处理做的,为了实现这个功能,必须要传递进去一个ExecutorPool,当scan返回result后,由这个线程池处理,其余的工作,全部委托给了RawAsyncTableImpl。

洋葱的第4.2层皮:RawAsyncTableImpl

RawAsyncTableImpl这个类负责处理全部的Table相关的核心逻辑,到这里,洋葱的第一层皮就剥掉了。
以上所有的类,即洋葱的第一层皮,使用的数据结构载体,即model类是一致的,即面向用户的Scan,Put,Get等model类。
再向下面,就是洋葱的第二层皮,这层皮的接口需要的数据,和第一层就不一样了。
以上所说的所有的操作,我们只是看到了一个大概的,对用户比较有好的异步接口实现,真正的IO请求逻辑在这个类里面展开。

洋葱的第4.2-1层皮:AsyncRpcRetryingCaller

AsyncRpcRetryingCaller的功能为控制向后端HBASE发送请求,从类的名字我们就能够看出来,这个主要是控制重复发送请求的类,Table接口后续的操作是依托AsyncRpcRetryingCaller及其实现类实现的,每次上层接口进行RPC请求,都会创建一个新的AsyncRpcRetryingCaller对象用以向后端进行请求,而对象的内部存储着所有需要向后端传递的信息。
AsyncRpcRetryingCaller对上层提供的方法为call(),没有参数,他也是上面所有组件的协调器,上面所说的所有的组件,都是他内部的属性。
这是一个抽象类,实现了一些基础的重试请求功能。
它的子类实现了不同的发送请求的功能,子类如下

  1. AsyncSingleRequestRpcRetryingCaller:实现了单个请求,例如get,put,delete的请求。
  2. AsyncAdminRequestRetryingCaller:admin控制请求的。
  3. AsyncMasterRequestRpcRetryingCaller:与master请求的。
  4. AsyncRegionReplicaReplayRetryingCaller:region请求。

这些子类都封装了不同的请求模式的请求功能,即AsyncRpcRetryingCaller中的抽象方法doCall().接下去的流程就到了thrift的rpc框架接口了。

洋葱的第4.2-1-1层皮:stub

stub是protobuf的一个通用rpc接口框架。他定义了整个RPC的顶层接口,接口有。

  1. Interface 层,这一层是给人使用的时候提供便利的,定义了很多不同的接口方法,当然,这些方法是描述文件里面定义的,这里只是给翻译成了java代码。
  2. RpcChannel层,这一层就是纯传输数据使用,上面定义的所有的接口,在这一层都被汇集到一个方法上面,向后方的server传递,从上面介绍RpcClient看,这个类的工厂类是RpcClient的实现类做的。

但是,protobuf只不过是定义了接口与如何序列化,真正的IO通信实现还是要自己写的,这就到了洋葱的第四层-RpcConnection。

洋葱的第4.2-1-1-1层皮:RpcConnection

我们从上面NettyRpcClient的介绍能看到,RpcConnection的创建是通过RpcClient的底层类实现的,通过AbstractRpcClient缓存RpcConnection。
RpcConnection是最终发送数据给后端Server的接口,发送的方法为

public void sendRequest(final Call call, HBaseRpcController hrc)

主要实现类为NettyRpcConnection,使用netty将数据传输给后端server。NettyRpcClient会生成NettyRpcConnection。

洋葱的第4.2-1-1-2层皮:Call对象

Call类是RpcConnection接口的参数类,内部有所有这次IO通讯所需要的信息,例如需要向后端传递的数据Message(protobuffer可序列化的顶层接口),本次Call的id,调用的是Rpc的哪个方法RpcClient,本次调用的callback信息,Span信息等。

洋葱的第4.2-1-1-3层皮: HBaseRpcController

HbaseRpcController是继承于protobuf的RpcController,protobuf的RpcController是用于控制单次Rpc请求的超时,记录异常等信息的,但在HbaseRpcController中,新增了携带Cell的功能,包括发送和接收,这是用于自定义压缩及编码需求的,但这项实现笔者认为比较怪异。

洋葱的第5层皮:Admin

不用说了,铁定是一层AsyncAdmin的包裹。

洋葱的第6层皮:AsyncAdmin

AsyncAdmin实现类有两个,AsyncHBaseAdmin和RawAsyncHbaseAdmin。

洋葱的第6.1层皮:AsyncHBaseAdmin

和上面的AsyncTable一样,包了一层pool。

洋葱的第6.2层皮:RawAsyncHBaseAdmin

这个的实现,和RawAsyncTableImpl是一样的,借用RetryingCaller实现后端通信,区别在于找的server的不一致,HMaster的server地址是设置在ZK上的,通过上面所说的AsyncConnectionImpl获得(这个类依赖ZKAsyncRegistry从ZK上获得)。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容