![8a3a1b69e68c0482a3828fef3298625a.png](https://i-blog.csdnimg.cn/blog_migrate/a1884ff0a13c4aed19b04259df45ae88.jpeg)
导读
在分布式存储系统中,节点间的通信是很重要的组成部分。如果不了解系统中的通信机制,就无法真正理解一套完整的读写流程。由于HBase年代久远(1.2.0版本),暂时使用的还是java原生的NIO,并没有使用Netty之类的框架。
本篇就来介绍下HBase中基于NIO的RPC通信。
1 RPC=protobuf+nio
HBase基于protobuf+nio实现rpc:基于protobuf预定义消息格式,基于nio做消息的传输以及服务器编写。
![ef53a8bcc865e17fe0e75f3910bb7006.png](https://i-blog.csdnimg.cn/blog_migrate/291f65e57a72a2b7574a036885c10e52.jpeg)
protobuf可以简单的理解成一种格式定义,定义好可以自动生成对应的接口类。开发者需要在客户端和服务端生成对应的实现类,客户端负责封装接口参数,通过socket发送到远程服务提供方;服务端负责实现接口的具体执行过程,在服务器上执行真正的操作,返回结果。这样调用者就可以像调用本地方法一样调用远程方法了。
![61b726795579170bdceef5331149307b.png](https://i-blog.csdnimg.cn/blog_migrate/81323fd359552cd6fa314813307cb5e9.jpeg)
HBase的服务端采用的是标准的Reactor模型:
Listener相当于acceptor,封装了Reader线程池,用于接收客户端的连接;
Scheduler中有不同的线程池用于专门处理任务;
Responder负责向客户端返回执行结果。
2 客户端详解
HBase的客户端与传统的数据库连接池类似,也是在内存中维护客户端与各个存储节点的连接,并配置对应的存活时间,存活时间范围内连接是可以复用的(这里的复用是指,不需要单独进行TCP的三次握手;通用的header,协议格式等都可以直接复用;直接调用对应的OutPutStream.write方法执行写操作就可以了)。因此如果想复用连接,就需要在逻辑业务上区分相同RegionServer的不同请求类型,HBase在protobuf中定义了不同的请求类型,如:Get,Mutate,Scan,Bulk,multi等,具体的可以参考protobuf中的定义。
总结来说,相同的RegionServer、相同的请求类型、存活时间范围内都可以共享连接,不需要重新建立连接。
整体的流程如下:当客户端需要发送请求时,先判断是否已经存在有效连接,如果不存在则重新建立。获得连接后执行发送操作,并阻塞等待。如果开启了异步发送,则会发送到另一个发送列表中,会有专门的线程执行发送操作。
![f5d1d9ddfefd8146d96e9b5694e5f549.png](https://i-blog.csdnimg.cn/blog_migrate/75d08869e3b5dcb68157e2bcb01b7251.jpeg)
客户端中比较重要的组件主要有下面几个:
RpcClientImpl:客户端主要实现类,内部维护了Connection的Map引用,其中Map的key由用户、方法服务名、目标地址等组成。
Connection:代表一次连接,内部封装了Socket。
![0d599e4cd0b6e122ea9af58c8967dd0c.png](https://i-blog.csdnimg.cn/blog_migrate/eadee453b901d3eedbebd445ee24c21c.jpeg)
整体流程如下:
1 当调用Callable时,获得RPC实例,并获得RpcClient实现类
2 调用Rpc的get方法
3 调用RpcClientImpl的call方法
4 获得连接Connection
5 执行发送
6 阻塞等待结果
![3142e0582255e00e73de059d35514900.png](https://i-blog.csdnimg.cn/blog_migrate/30a138dc7cf8da90163223c2a1460813.jpeg)
3 服务端详解
服务端的设计明显就要复杂很多,首先是Rpc的服务器都封装在RpcServer中,当HRegionServer启动时,会执行初始化。
![6691d0d575e853564e42f0ef9e54ebb1.png](https://i-blog.csdnimg.cn/blog_migrate/d74099bc54d533064562f387a75b248f.jpeg)
Server中包含三个主要的部分:
Listener,负责监听消息,内部封装Reader线程池异步接收处理、
Responder,负责回复、
RpcScheduler,负责根据不同的策略分发任务
![8d7fb22b2093d6bde2c585c84f5cbd4e.png](https://i-blog.csdnimg.cn/blog_migrate/3267530969577c81935ea4f6cc48094d.jpeg)
整体流程如下:
1 HRegionServer启动,开启RsRpcServerices服务
2 初始化RpcServer,初始化内部组件:Listener、scheduler、responder等
3 listener接收请求,内部Reader线程池轮训处理,执行readprocess,交给scheduler进行分发
4 scheduler分发执行任务,调用rpcserver的call方法
5 调用rsrpcservices中的实现方法,执行处理
6 结果放入返回队列中,返回
![6e6fad397991d94efcee3c995a672953.png](https://i-blog.csdnimg.cn/blog_migrate/63815e39113414b43e8377b4ee7682b9.jpeg)
4 总结
第一次阅读NIO相关的服务器编程,关于NIO的技术细节还不是很熟练,因此有很多Selector互相调用触发的代码看的有点蒙。不过总体的流程梳理的差不多,这些细节也就不过多花精力了。
总结一下:对于HBase的Client端,连接使用NIO的Socket实现,相同主机相同类型的请求复用连接,阻塞等待连接返回结果;对于HBase的Server端,使用Reader线程池异步接收消息执行读取操作;使用Scheduler进行消息的分发处理;使用Responder合并处理结果,批量返回。
往期推荐:
HBase源码:如何设计高效的分布式客户端mp.weixin.qq.com![526b5f75733688e677000fc47024ff3d.png](https://i-blog.csdnimg.cn/blog_migrate/72df98d0abd84881cf775130e991e841.jpeg)
![30cfb7764e1d20ce854c19f14ff86a4c.png](https://i-blog.csdnimg.cn/blog_migrate/a67b41ef4d03caacb77fd5ca985c8c77.jpeg)
![ca726c1b5e730ee8c2d07b5bceaa18e9.png](https://i-blog.csdnimg.cn/blog_migrate/36ba0192acb66317a0b1fe723f2e18c7.jpeg)