Hadoop RPC机制中Server类的实现:基于Java NIO

Posted JKerving

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hadoop RPC机制中Server类的实现:基于Java NIO相关的知识,希望对你有一定的参考价值。

RPC服务器端代码在获取Server对象后,Server对象就要真正开始干活啦。服务器端代码马上会启动Server对象监听网络上的RPC请求并触发响应操作。
这个监听可不是一直阻塞式的,这样会导致Server端效率极低,啥事也干不了,干等着。Hadoop这样一个大型分布式框架不会做这样的傻事。
为了提高性能,Server类采用了很多技术来提高并发能力,包括线程池、Java NIO提供的Reactor模式等,尤其重要的就是这个Reactor模式贯穿了整个Server的设计。

Reactor模式

RPC服务器端代码的处理流程与所有网络程序服务器端的处理流程类似,都分为5个步骤:读取请求、反序列化请求、处理请求、序列化响应、返回响应。对于网络服务器端程序来说,如果对每个请求都构造一个线程响应,那么在负载增加时性能会下降得很快;而如果只用少量线程响应,又会在IO阻塞时造成响应流程停滞、吞吐率降低。所以为了解决上述问题,Reactor模式出现了。
Reactor模式是一种广泛应用在服务器端的设计模式,也是一种基于事件驱动的设计模式。Reactor模式的处理流程是:应用程序向一个中间人注册IO事件,当中间人监听到这个IO事件发生后,会通知并唤醒应用程序处理这个事件。这里的中间人其实是一个不断等待和循环的线程,它接受所有应用程序的注册,并检查应用程序注册的IO事件是否就绪,如果就绪则通知应用程序进行处理。
一个简单的基于Reactor模式的网络服务器设计可以如下图所示,包括reactor、acceptor、以及handler等模块。reactor负责监听所有的IO事件,当检测到一个新的IO事件发生时,reactor会唤醒这个事件对应的模块处理。acceptor则负责响应Socket连接请求事件,acceptor会接收请求建立连接,之后构造handler对象。handler对象则负责向reactor注册IO读事件,然后从网络上读取请求并执行对应的业务逻辑,最后发回响应。使用Reactor模式的服务器响应客户端请求的流程可以分为以下几个步骤:

  • 客户端发送Socket连接请求到服务器,服务器的reactor对象监听到了这个IO请求,由于acceptor对象在reactor对象上注册了Socket连接请求的IO事件,所以reactor会触发acceptor对象响应Scoket连接请求。
  • acceptor对象会接收来自客户端的Socket连接请求,并为这个连接创建一个handler对象,handler对象的构造方法会在reactor对象上注册IO读事件。
  • 客户端在连接建立成功之后,会通过Socket发送RPC请求。RPC请求到达reactor后,会由reactor对象dispatch到对应的handler对象处理。
  • handler对象会从网络上读取RPC请求,然后反序列化请求并执行对应的业务处理逻辑,最后将响应消息序列化并通过Socket返回客户端。到这里,一个完整的RPC请求流程就结束了。

采用了基于事件驱动模式的Reactor结构,服务器只有在指定的IO事件发生时才会调用acceptor以及handler对象提供的方法执行业务逻辑,避免了在IO上无谓的阻塞。但是由于上述设计中服务器端只有一个线程,所以就要求handler中读取请求、执行请求以及发送响应的流程必须能够迅速处理完毕,如果在某个流程阻塞了,那么整个服务器逻辑全部阻塞。所以需要进一步改进架构,采用多线程处理业务逻辑。

多线程改造

对于handler处理RPC请求的5个步骤,我们可以将占用时长较长的读取请求部分以及业务逻辑处理部分交给两个独立的线程池处理。下图给出了使用多线程的Reactor模式的网络服务器架构,reader线程池包含若干个执行读取RPC请求任务的Reader线程,它们会在Reactor上注册读取RPC请求的IO事件,然后从网络中读取RPC请求,并将RPC请求封装在一个Call对象中,最后将Call对象放入共享消息队列MQ中。而handler线程池则包含若干个处理业务逻辑的Handler线程,它们会不断地从共享消息队列MQ中取出PRC请求,然后执行业务逻辑并向客户端返回响应。这种架构保证了IO事件的监听和分发,RPC请求的读取和响应是在不同的线程中执行的,大大提高了服务器的并发性能。

多Reactor多线程模式

采用了多线程的Reactor模式后,IO事件的监听、RPC请求的读取和处理就可以并发地进行,但是对于NameNode这种分布式集群中的Master节点来说,同一时间可能有非常多的Socket连接请求以及RPC请求到达,这就可能造成Reactor在处理和分发这些IO事件时出现拥塞,导致服务器整体性能降低。所以我们可以将上图中的一个Reactor对象扩展成多个Reactor,它们分别用于并发地监听不同的IO事件,这样就提高了IO事件的处理效率,同时提高了RPC服务器的性能。
下图给出了使用多个Reactor的服务器结构,这里的mainReactor负责监听Socket连接事件,readReactor监听IO读事件,respondSelector负责监听IO写事件。由于同一时间到达RPC服务器的RPC请求可能很多,也就会造成一个readReactor要同时处理多个IO读事件的分发,当系统负载达到一定量时,readReactor就有可能成为瓶颈。所以我们可以构造多个readReactor对象,不同的Reader线程会根据一定的逻辑到不同的readReactor上注册IO读事件。当acceptor建立了Socket连接后,会从readers线程池中取出一个Reader线程触发读取RPC请求的流程。Reader线程会根据一定的逻辑选出一个readReactor并在这个readReactor对象上注册读取RPC请求的IO事件,之后就会由该readReactor在网络上监听是否有RPC请求到达,并触发Reader线程读取流程。当Handler成功地处理一个RPC请求后,它会向respondSelector注册写RPC响应IO事件,当Socket输出流管道可以写数据时,Sender类就可以将响应信息发回客户端了。

以上是关于Hadoop RPC机制中Server类的实现:基于Java NIO的主要内容,如果未能解决你的问题,请参考以下文章

Hadoop RPC机制中Server类的实现:基于Java NIO

RPC的实现与远程调用的实现机制

RPC的实现与远程调用的实现机制

3 weekend110的hadoop中的RPC框架实现机制 + hadoop中的RPC应用实例demo

Hadoop RPC机制

Hadoop RPC机制