RPC通信功能实现
RPC通信功能实现
HBase的RPC通信功能主要基于Protobuf和NIO这两个组件来实现。在通信管道上选择的是protobuf对外声明的BlockingRpcChannel(堵塞式),其callBlockingMethod方法决定了client与服务端的交互行为。比方採用什么样的方法进行通信以及通信报文的格式规则都是通过该方法来描写叙述的。
HBase对外声明了BlockingRpcChannelImplementation实现类用于实现BlockingRpcChannel接口的业务逻辑。其在通信方式的选择上採用的是Socket通信。在通信的服务端通过RpcServer来构建ServerSocket。而在client使用RpcClient来构建与服务端通信的Socket,依照功能职责的不同,RpcServer可划分成3大组件。当中:
Listener负责监听client的连接请求
在Listener的内部主要封装着一个ServerSocketChannel以及多个Reader线程。当中ServerSocketChannel主要负责接收client的连接请求,请求被响应前,会暂存于等待队列中,等待队列的长度通过hbase.ipc.server.listen.queue.size參数来设置(默觉得128)。针对每一个已建立的连接。系统还会实时检測其空暇时间,假设空暇时间超过2秒(即2秒内client没有再次通过该连接来发送请求,而且之前的请求操作已经处理完成),系统会将该连接进行关闭,时间阀值是通过hbase.ipc.client.connection.maxidletime參数来控制的。
当请求信息到达后,其会派遣合适的Reader进行读取(基于轮训的方式来使每一个Reader的负载可以均衡),Reader线程的数量是通过hbase.ipc.server.read.threadpool.size參数来指定的,默觉得10个。线程启动后会进入堵塞状态直至client请求操作的到来。client向服务端发送的通信报文是依照一定格式进行组织的,如图所看到的:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
当client与服务端进行初次握手时,其会向服务端发送RPCHeader报文。以便服务端可以对client的连接请求做校验处理。
假设校验结果满足下面规则,说明该请求操作是合法的:
(1)前4个字节信息为HBas;
(2)第5个字节(VERSION信息)的值为0;
(3)在没有启用security的情况下(hbase.security.authentication属性值不为kerberos)。第6个字节的值为80。
接着。client会向服务端发送ConnectionHeader报文,通过它来封装client所请求的服务。
ConnectionHeader是通过使用protobuf来完毕序列化处理的,其protocol声明例如以下:
message ConnectionHeader {
optional UserInformation user_info = 1;
optional string service_name = 2;
optional string cell_block_codec_class = 3;
optional string cell_block_compressor_class = 4;
}服务端收到该请求消息之后,可通过其service_name属性来推断client所要訪问的服务名称,从而定位到详细的服务。
确定了详细的服务之后,client便可持续向服务端发送Request报文,通过它来定位将要运行服务的哪一个方法。
方法名称是通过RequestHeader来封装的。其属于Request报文的一部分,如图所看到的:
RequestHeader相同是採用protobuf进行序列化处理。其protocol声明例如以下:
message RequestHeader {
optional uint32 call_id = 1;
optional RPCTInfo trace_info = 2;
optional string method_name = 3;
optional bool request_param = 4;
optional CellBlockMeta cell_block_meta = 5;
optional uint32 priority = 6;
}其method_name属性用于定位将要运行的方法名称,方法參数是通过Param报文来封装的。除此之外,client还可向服务端传递一些KeyValue数据(比方Replication功能会用到这些数据)。这些数据会序列化到CellBlock报文里。
Reader线程读取到这些信息后開始构造CallRunner对象。并将其赋予空暇的Handler进行处理。
Handler负责处理client的请求操作
从服务端的角度观察,client的全部请求都可封装成CallRunner对象,假设把Reader看做是CallRunner的生产者,那么Handler便是消费者。为了加快服务端的响应效率,RpcServer是同意同一时候存在多个消费者的,以此来并发消费全部的CallRunner产品。然而CallRunner产品在全部的消费者之间应当怎样做到合理分配?这主要是通过RpcScheduler来调度的。HBase对外声明了两种RpcScheduler的功能实现类。当中HMaster使用的是FifoRpcScheduler,而HRegionServer使用的SimpleRpcScheduler。
FifoRpcScheduler
基于线程池来消费全部的CallRunner产品,CallRunner的消费顺序採用FIFO原则(依照产出的先后顺序依次进行消费)。针对每一个CallRunner产品,系统都会开启一个Handler线程负责对其进行消费处理,线程池所能同意的最大并发数是由详细的服务来对外进行声明的,如HMaster默认情况下同意25个并发Handler(通过hbase.master.handler.count參数进行设置)。
SimpleRpcScheduler
採用该策略进行调度处理以后,系统会依据不同的请求类型将全部的CallRunner产品划分成3组:
(1)假设其封装的请求是基于meta表格的操作,将其划分到priorityExecutor组里。
(2)假设其封装的请求是基于用户表格的操作,将其划分到callExecutor组里;
(3)假设其封装的是replication请求,将其划分到replicationExecutor组里。
然后为每个产品组分配数量不等的Handler,让Handler仅仅消费指定组中的产品。不同的产品组所分配的Handler数量相同是由详细的服务来对外声明的。拿HRegionServer举例:
分配给priorityExecutor组的Handler数量通过hbase.regionserver.metahandler.count參数来指定。默觉得10个;
分配给callExecutor组的Handler数量通过hbase.regionserver.handler.count參数来指定,默觉得30个;
分配给replicationExecutor组的Handler数量通过hbase.regionserver.replication.handler.count參数来指定。默觉得3个。
每个产品组还可细分成多个产品队列,默认情况下每个产品组仅仅包括一个产品队列。
这样产品组中的全部Handler都会去竞争该队列中的资源。为了防止竞争慘烈的情况发生,可将每个产品组划分成多个产品队列,让每个Handler仅仅去抢占指定队列中的资源。
在HRegionServer中,可通过例如以下方法来计算callExecutor组能够划分成多少个产品队列:
Math.max(1,hbase.regionserver.handler.count*hbase.ipc.server.callqueue.handler.factor)
当中hbase.ipc.server.callqueue.handler.factor属性值默觉得0。即在默认情况下仅仅将该产品组划分成一个产品队列。
单个产品队列的容量并非按需使用无限增长的,HBase对其长度及空间大小都做了对应的阀值控制,当中:
hbase.ipc.server.max.callqueue.length用于限制产品队列的长度(默觉得handler数乘以10)
hbase.ipc.server.max.callqueue.size用于限制产品队列的空间大小(默觉得1G)
成功将CallRunner产品分配给Handler之后,该Handler開始对其进行消费处理。消费过程主要是通过调用RpcServer的call方法来运行指定服务的对应方法。并通过Responder将方法的运行结果返回给client。
Responder负责将服务端的处理结果返回给client
服务端返回给client的通信报文是依照例如以下格式进行组织的:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
当中ResponseHeader是採用protobuf进行序列化的。其protocol声明例如以下:
message ResponseHeader {
optional uint32 call_id = 1;
optional ExceptionResponse exception = 2;
optional CellBlockMeta cell_block_meta = 3;
}其内部主要封装了服务端的运行异常信息,以及CellBlock的元数据信息;Result用于封装运行方法的返回结果。其序列化方法须要依据详细的返回值类型来做决定。CellBlock用于封装服务端所返回的KeyValue数据(如scan操作的查询结果)。
客户端发送请求消息之后,会进入循环等待状态,直至服务端返回运行结果,假设等待时间超过10秒。则系统会觉得该请求失败,将开启重试或关闭连接(假设hbase.ipc.client.connect.max.retries參数值为0)。
配置參数
服务端相关配置例如以下:
hbase.ipc.server.listen.queue.size
存放连接请求的等待队列长度,默认与ipc.server.listen.queue.size參数值同样,为128个。
hbase.ipc.server.tcpnodelay
是否在TCP通信过程中启用Nagle算法,默认不启用。
hbase.ipc.server.tcpkeepalive
是否启用TCP的keepalive机制。通过心跳包来推断连接是否断开,默认启用。
hbase.ipc.server.read.threadpool.size
Reader线程数。默觉得10个。
hbase.ipc.server.max.callqueue.size
单个消费队列所同意的存储空间上限(默觉得1GB),超过该上限client会抛出下面异常:
Call queue is full, is ipc.server.max.callqueue.size too small?
hbase.ipc.server.max.callqueue.length
单个消费队列的长度限制,默认值为10倍的Handler数。
hbase.ipc.server.callqueue.handler.factor
该參数用于决定消费队列的个数。
hbase.ipc.server.callqueue.read.share
读Handler数占总Handler数的比例。
client相关配置例如以下:
hbase.ipc.ping.interval
client与服务端的心跳时间间隔,以及Socket的默认读写超时时间(HBase的其它一些參数会覆盖该值,如hbase.rpc.timeout)。
hbase.client.rpc.codec
CellBlock报文内容的编码/解码器,默认与hbase.client.default.rpc.codec的參数值同样,为org.apache.hadoop.hbase.codec.KeyValueCodec。
假设将hbase.client.default.rpc.codec设置成空字符串,而且不正确hbase.client.rpc.codec參数进行设置,则在rpc通信过程中将不在使用CellBlock报文对KeyValue进行序列化。而是将其序列化到protobuf的message里(Param或Result)。
hbase.client.rpc.compressor
CellBlock报文内容的压缩/解压缩算法,默认不採用压缩。
hbase.ipc.socket.timeout
client尝试与服务端建立连接的超时时间,默认与ipc.socket.timeout同样为20秒。
hbase.rpc.timeout
client对RegionServer的rpc请求超时时间。
hbase.client.pause
Socket连接失败后。会休眠一段时间。然后在又一次连接,该參数用于指定休眠多久。默觉得0.1秒。
hbase.ipc.client.connect.max.retries
当client与服务端的连接出现错误时。通过该參数来指定重试次数,默觉得0(不重试)。
hbase.ipc.client.connection.maxidletime
client与服务端的连接空暇时间超过该数值时(即指定时间范围内,client没有收到服务端的响应信息)。系统会将该连接关闭,默认的响应超时时间为10秒。
hadoop.rpc.socket.factory.class.default
SocketFactory实现类,默觉得org.apache.hadoop.net.StandardSocketFactory,其createSocket方法会创建SocketChannel用于NIO通信。
调用方法
在client可通过例如以下代码来构建所需服务,拿ClientService举例:
RpcClient rpcClient = new RpcClient(conf, clusterId);

BlockingRpcChannel channel =
rpcClient.createBlockingRpcChannel(serverName
, user
, rpcTimeout
);
ClientService.BlockingInterface stub = ClientService.newBlockingStub(channel);
首先构建RpcClient,当中clusterId属性可从Zookeeper的/hbase/hbaseid节点中读取;

serverName用于定位服务端地址,可通过ServerName.valueOf("${host},${port},${startCode}")方法来构建,如ServerName.valueOf("localhost,60020,1422961250317");

user为client的请求用户,可通过User.getCurrent()来获取。

rpcTimeout为rpc请求的超时时间。
在服务端可通过例如以下代码来公布服务,相同拿ClientService举例:
List<BlockingServiceAndInterface> services =
new ArrayList<BlockingServiceAndInterface>();
services.add(new BlockingServiceAndInterface(
ClientService.newReflectiveBlockingService(regionServer),
ClientService.BlockingInterface.class));
RpcServer rpcServer = new RpcServer(serverInstance
, name
, services
,
isa
, conf, scheduler
);
rpcServer.start();
构造ClientService实例。通过其newReflectiveBlockingService方法。方法參数为HRegionServer实例,事实上现了ClientService.BlockingInterface接口;

serverInstance为服务进程实例,这里为HRegionServer。

name为服务进程名称。

services为服务进程中包括的服务列表;

isa为服务的通信地址。

scheduler为rpc请求调度器,眼下有两种实现:FifoRpcScheduler和SimpleRpcScheduler。
RPC通信功能实现的更多相关文章
- RPC通信框架——RCF介绍
现有的软件中用了大量的COM接口,导致无法跨平台,当然由于与Windows结合的太紧密,还有很多无法跨平台的地方.那么为了实现跨平台,支持Linux系统,以及后续的分布式,首要任务是去除COM接口. ...
- RPC通信框架——RCF介绍(替换COM)
阅读目录 RPC通信框架 为什么选择RCF 简单的性能测试 参考资料 总结 现有的软件中用了大量的COM接口,导致无法跨平台,当然由于与Windows结合的太紧密,还有很多无法跨平台的地方.那么为了实 ...
- RPC通信原理(未完,先睡觉)
一 背景 OpenStack 各组件之间是通过 REST 接口进行相互通信,比如Nova.Cinder.Neutron.Glance直间的通信都是通过keystone获取目标的endpoint,即ap ...
- 【Java】分布式RPC通信框架Apache Thrift 使用总结
简介 Apache Thrift是Facebook开源的跨语言的RPC通信框架,目前已经捐献给Apache基金会管理,由于其跨语言特性和出色的性能,在很多互联网公司得到应用,有能力的公司甚至会基于th ...
- 基于oslo_messaging的RPC通信
oslo_messaging源于Openstack的一个经典的模块,用以实现服务间的RPC通信.Client端将数据放入rabbitmq中,server端从消息队列中获取传送数据. oslo.mess ...
- openstack RPC通信
openstack RPC通信 OpenStack 的主要组件有 Nova.Cinder.Neutron.Glance 等,分别负责云平台的计算.存储.网络资源管理.openstack 各组件之间是通 ...
- RPC通信框架——RCF介绍
现有的软件中用了大量的COM接口,导致无法跨平台,当然由于与Windows结合的太紧密,还有很多无法跨平台的地方.那么为了实现跨平台,支持Linux系统,以及后续的分布式,首要任务是去除COM接口. ...
- SpringBoot2+Netty打造通俗简版RPC通信框架(升级版)
背景 上篇文章我简单的介绍了自己打造的通俗简版RPC通信框架,这篇是对简版的增强~ 如果大家对此项目还感兴趣的话,可到码云上瞄瞄:Netty-RPC 上 ...
- SpringBoot2+Netty打造通俗简版RPC通信框架
2019-07-19:完成基本RPC通信! 2019-07-22:优化此框架,实现单一长连接! 2019-07-24:继续优化此框架:1.增加服务提供注解(带版本号),然后利用Spring框架的在启动 ...
随机推荐
- [Luogu 2678] noip15 子串
[Luogu 2678] noip15 子串 题目描述 有两个仅包含小写英文字母的字符串 A 和 B.现在要从字符串 A 中取出 k 个互不重叠的非空子串,然后把这 k 个子串按照其在字符串 A 中出 ...
- fastjson读取json配置文件
fastjson读取json配置文件: ClassLoader loader=FileUtil.class.getClassLoader(); InputStream stream=loader.ge ...
- Jquery和Ajax的关系!
Jquery是一种JavaScript框架,而Ajax(Asynchronous JavaScript and XML)是异步JavaScript和XML. Jquery是JavaScript的框架, ...
- Java系列学习(十二)-开始Eclipse
1.用Eclipse来写一个HelloWorld (1)选择工作空间 工作空间其实就是我们写的源代码所在的目录 (2)创建一个Java项目 [File-New-Java Project] (3)创建包 ...
- Hive扩展功能(五)--HiveServer2服务高可用
软件环境: linux系统: CentOS6.7 Hadoop版本: 2.6.5 zookeeper版本: 3.4.8 主机配置: 一共m1, m2, m3这五部机, 每部主机的用户名都为centos ...
- jQuery——事件操作
事件绑定 1.简单事件绑定 $("button").click(function () {})//可重复绑定,不会被层叠 2.bind():不推荐使用 $("button ...
- 控制台——EventLog实现事件日志操作
我们应该如何通过写代码的方式向其中添加“日志”呢? 在操作之前,先明确几个概念: 1:事件日志名(logName):“事件查看器”中的每一项,如“应用程序”.“Internet Explorer”.“ ...
- java攻城狮之路--复习xml&dom_pull编程续
本章节我们要学习XML三种解析方式: 1.JAXP DOM 解析2.JAXP SAX 解析3.XML PULL 进行 STAX 解析 XML 技术主要企业应用1.存储和传输数据 2.作为框架的配置文件 ...
- 【译】x86程序员手册08 -2.6中断和异常
2.6 Interrupts and Exceptions 中断和异常 The 80386 has two mechanisms for interrupting program execution: ...
- 【VHDL】组合逻辑电路和时序逻辑电路的区别
简单的说,组合电路,没有时钟:时序电路,有时钟. ↓ 也就是说,组合逻辑电路没有记忆功能,而时序电路具有记忆功能. ↓ 在VHDL语言中,不完整条件语句对他们二者的影响分别是什么?组合逻辑中可能生成锁 ...