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="">

    1. 当client与服务端进行初次握手时,其会向服务端发送RPCHeader报文。以便服务端可以对client的连接请求做校验处理。

      假设校验结果满足下面规则,说明该请求操作是合法的:

      (1)前4个字节信息为HBas;

      (2)第5个字节(VERSION信息)的值为0;

      (3)在没有启用security的情况下(hbase.security.authentication属性值不为kerberos)。第6个字节的值为80。

    2. 接着。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所要訪问的服务名称,从而定位到详细的服务。

    3. 确定了详细的服务之后,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。

    1. FifoRpcScheduler

      基于线程池来消费全部的CallRunner产品,CallRunner的消费顺序採用FIFO原则(依照产出的先后顺序依次进行消费)。针对每一个CallRunner产品,系统都会开启一个Handler线程负责对其进行消费处理,线程池所能同意的最大并发数是由详细的服务来对外进行声明的,如HMaster默认情况下同意25个并发Handler(通过hbase.master.handler.count參数进行设置)。

    2. 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。

    3. 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)。

配置參数

  • 服务端相关配置例如以下:

    1. hbase.ipc.server.listen.queue.size

      存放连接请求的等待队列长度,默认与ipc.server.listen.queue.size參数值同样,为128个。

    2. hbase.ipc.server.tcpnodelay

      是否在TCP通信过程中启用Nagle算法,默认不启用。

    3. hbase.ipc.server.tcpkeepalive

      是否启用TCP的keepalive机制。通过心跳包来推断连接是否断开,默认启用。

    4. hbase.ipc.server.read.threadpool.size

      Reader线程数。默觉得10个。

    5. hbase.ipc.server.max.callqueue.size

      单个消费队列所同意的存储空间上限(默觉得1GB),超过该上限client会抛出下面异常:

      Call queue is full, is ipc.server.max.callqueue.size too small?

    6. hbase.ipc.server.max.callqueue.length

      单个消费队列的长度限制,默认值为10倍的Handler数。

    7. hbase.ipc.server.callqueue.handler.factor

      该參数用于决定消费队列的个数。

    8. hbase.ipc.server.callqueue.read.share

      读Handler数占总Handler数的比例。

  • client相关配置例如以下:

    1. hbase.ipc.ping.interval

      client与服务端的心跳时间间隔,以及Socket的默认读写超时时间(HBase的其它一些參数会覆盖该值,如hbase.rpc.timeout)。

    2. 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)。

    3. hbase.client.rpc.compressor

      CellBlock报文内容的压缩/解压缩算法,默认不採用压缩。

    4. hbase.ipc.socket.timeout

      client尝试与服务端建立连接的超时时间,默认与ipc.socket.timeout同样为20秒。

    5. hbase.rpc.timeout

      client对RegionServer的rpc请求超时时间。

    6. hbase.client.pause

      Socket连接失败后。会休眠一段时间。然后在又一次连接,该參数用于指定休眠多久。默觉得0.1秒。

    7. hbase.ipc.client.connect.max.retries

      当client与服务端的连接出现错误时。通过该參数来指定重试次数,默觉得0(不重试)。

    8. hbase.ipc.client.connection.maxidletime

      client与服务端的连接空暇时间超过该数值时(即指定时间范围内,client没有收到服务端的响应信息)。系统会将该连接关闭,默认的响应超时时间为10秒。

    9. hadoop.rpc.socket.factory.class.default

      SocketFactory实现类,默觉得org.apache.hadoop.net.StandardSocketFactory,其createSocket方法会创建SocketChannel用于NIO通信。

调用方法

  1. 在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请求的超时时间。

  2. 在服务端可通过例如以下代码来公布服务,相同拿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通信功能实现的更多相关文章

  1. RPC通信框架——RCF介绍

    现有的软件中用了大量的COM接口,导致无法跨平台,当然由于与Windows结合的太紧密,还有很多无法跨平台的地方.那么为了实现跨平台,支持Linux系统,以及后续的分布式,首要任务是去除COM接口. ...

  2. RPC通信框架——RCF介绍(替换COM)

    阅读目录 RPC通信框架 为什么选择RCF 简单的性能测试 参考资料 总结 现有的软件中用了大量的COM接口,导致无法跨平台,当然由于与Windows结合的太紧密,还有很多无法跨平台的地方.那么为了实 ...

  3. RPC通信原理(未完,先睡觉)

    一 背景 OpenStack 各组件之间是通过 REST 接口进行相互通信,比如Nova.Cinder.Neutron.Glance直间的通信都是通过keystone获取目标的endpoint,即ap ...

  4. 【Java】分布式RPC通信框架Apache Thrift 使用总结

    简介 Apache Thrift是Facebook开源的跨语言的RPC通信框架,目前已经捐献给Apache基金会管理,由于其跨语言特性和出色的性能,在很多互联网公司得到应用,有能力的公司甚至会基于th ...

  5. 基于oslo_messaging的RPC通信

    oslo_messaging源于Openstack的一个经典的模块,用以实现服务间的RPC通信.Client端将数据放入rabbitmq中,server端从消息队列中获取传送数据. oslo.mess ...

  6. openstack RPC通信

    openstack RPC通信 OpenStack 的主要组件有 Nova.Cinder.Neutron.Glance 等,分别负责云平台的计算.存储.网络资源管理.openstack 各组件之间是通 ...

  7. RPC通信框架——RCF介绍

    现有的软件中用了大量的COM接口,导致无法跨平台,当然由于与Windows结合的太紧密,还有很多无法跨平台的地方.那么为了实现跨平台,支持Linux系统,以及后续的分布式,首要任务是去除COM接口. ...

  8. SpringBoot2+Netty打造通俗简版RPC通信框架(升级版)

    背景         上篇文章我简单的介绍了自己打造的通俗简版RPC通信框架,这篇是对简版的增强~         如果大家对此项目还感兴趣的话,可到码云上瞄瞄:Netty-RPC         上 ...

  9. SpringBoot2+Netty打造通俗简版RPC通信框架

    2019-07-19:完成基本RPC通信! 2019-07-22:优化此框架,实现单一长连接! 2019-07-24:继续优化此框架:1.增加服务提供注解(带版本号),然后利用Spring框架的在启动 ...

随机推荐

  1. python 生成器函数.推导式.生成器表达式

    一.生成器 什么是生成器,生成器的实质就是迭代器 在python中有三种方式来获取生成器: 1.通过生成器函数 2.通过各种推导式来实现生成器 3.通过数据的转换也可以获取生成器 1 def func ...

  2. firefox 附加组件栏安装

    firefox 在升级到 30的版本后,发现附加组件栏不兼容了. 搜索组件,add-on bar 会得到一个 new add-on bar的组件,安装完后发现上面不显示ip, 后来才发现,应该安装Th ...

  3. 对“空引用”说bye-bye

    大家可能经常遇到这种情况:当一个对象为null时,调用这个对象的方法或者属性时,就会报错:“Object reference not set to an instance of an object.” ...

  4. Java jre7及以上版本中的switch支持String的实现细节

    Java7中的switch支持String的实现细节 作者: zsxwing 更新: 2013-03-04 21:08:02 发布: 2012-04-26 13:58:19 在Java7之前,swit ...

  5. boolean b=true?false:true==true?false:true;

    下列代码的输出结果是_____ boolean b=true?false:true==true?false:true;System.out.println(b); 答案:false 题目来源:携程20 ...

  6. 00-SQLite的SQL语法

    SQLite的SQL语法 SQLite库可以解析大部分标准SQL语言.但它也省去了一些特性并且加入了一些自己的新特性.这篇文档就是试图描述那些SQLite支持/不支持的SQL语法的.查看关键字列表. ...

  7. 移动web——媒体查询

    基本概念 响应式开发在没有媒体查询前,也可以通过js来实现,但是人们基本不会考虑,特别繁琐.在出现了媒体查询,才开始逐渐推广响应式.实际开发中,在时间与金钱充足的情况下还是别做响应式,影响性能,维护麻 ...

  8. vs for Mac中的启用Entity Framework Core .NET命令行工具

    在vs for Mac的工具菜单中已没有了Package Manager Console. 我们可以通过以下方法使用Entity Framework Core .NET命令行工具: 1.添加Nuget ...

  9. linux调试环境时常用的命令 及 常识

    1.查找文件或文件夹 所在的目录 sudo find / -name your_filename 示例: wangju@wangju-HP--G4:~$ sudo find / -name .jenk ...

  10. win10 ubuntu18双系统环境搭建

    感谢前辈辛勤总结,根据这3篇文章成功配置了双系统 https://blog.csdn.net/qq_24624539/article/details/81775635 https://blog.csd ...