|
什么是RPC?RPC如何工作,本文将讨论这些话题
RPC(remote produce call)是一种常见的分布式网络通信协议,它允许本地计算机远程调用远程计算机的子程序,同时隐藏了具体了通信细节,使得用户无需考虑交互式编程,对于分布式系统hadoop,hadoop——RPC作为MR,YARN,HDFS公用的通信模块,保证其轻量级,高性能,可控性显得尤为重要(java RMI重量级过大,且可控性太少)一个典型的RPC架构包含以下几个部分:
1,客户端程序:请求的发出者
2,stub程序:可看着是代理程序(也可理解为存根),他使得远程服务向本地调用一样透明,在客户端,它将请求信息通过通信模块发送给服务器端,当服务器解码请求-调用服务-编码应- 发送应答后,客户端会解码对应结果(hadoop提供两组序列化框架:avro和google开源的 protocol buffers,默认为avro)
3,通信模块:两个相互协作的通信模块实现请求-应答协议,他们只负责在client和server间传递请求和应答消息,一般不会对数据包做任何处理(request-response 协议一般包含同步和异步两种)
4,调度程序(只存在与服务端):调度程序接收来自通信模块的请求消息,并根据其中的标识选择一个stub程序进行处理,通常客户端并发量过大时,会采用线程池提高处理效率
5,服务程序:请求的处理者
一个完成的RPC请求过程如下:
客户端程序—请求—>sub程序—请求—>通信模块 ——远程请求——————>通信模块—调度程序—stub程序—>服务程序
一个完成的RPC响应过程是上述过程的逆向执行,不再赘述
下面从从实现机制分析RPC
具体实现机制如下:
1,序列化:序列化的作用是将结构化对象转化为字节流以便于网络传输或持久存储,hadoop提供3种序列化机制(protocol buffers,avro,实现writable接口)
2,函数调用层:hadoop RPC采用java反射机制和动态代理
3,网络传输层:hadoop采用了基于TCP/IP的socket机制
4,服务器处理框架:服务器端处理框架可被抽象成网络I/O模型,他描述了Client-server的交互方式,它的设计直接决定着服务器处理并发的能力,常见的网络I/O模型有
阻塞式I/O,非阻塞I/O,事件驱动I/O(hadoop采用)
如何使用hadoop RPC?
hadoop RPC对外主要提供两种接口:
1,ProtocolProxy getProxy / waitProxy(...),构造一个客户端代理对象(实现了某个协议),用于向服务器发送RPC请求
2,Server RPC.builder(conf).build(); 为某个协议(java接口的一个实例)构造一个服务器对象,用于处理客户端发送的请求
通常而言,使用RPC分为以下几步:
1,定义RPC协议:
hadoop 用户自定义RPC协议需要实现VersionProtocol接口(默认情况下,不同版本号的Client和Server无法通信)
并定义下面方法
int add(int v1,int v2);
2,实现自定义的RPC接口
编写一个java实体类实现自定义的RPC协议
3,构造并启动RPC server:
直接使用静态类Builder构造一个RPC Server,并调用函数 start()启动该Server
实例代码如下:
Server server = new RPC.Builder(conf).setProtocol(ClientProtocol.class).setInstance(new ClienProtocolImpl()).setBindAddress(ADDRESS).setProt(0)
server.start();
4,构造RPC Client并发送RPC请求
使用静态方法getProxy构造客户端代理对象,直接通过代理对象调用远程方法,代码如下:
proxy =(ClientProtocol)RPC.getproxy(ClientProtocol.class,ClientProtocol.versionID,addr,conf);
int result = proxy.add(5,6);
hadoop RPC类详解
hadoop RPC主要由三大类组成,即RPC(对外编程接口),Client(客户端),Server(服务端)
RPC:
RPC类定义了一系列构建和销毁RPC客户端的方法,构建方法分为getproxy,waitForProxy,摧毁的方法:stopProxy
RPC服务器的构建则由静态内部类RPC.Builder
RPC.setProtocolEngine(...)修改采用的序列化方式
Client:客户端
Client的主要功能是发送远程过程调用信息并将接收执行结果
Client内部有两个重要的类:Call,Connection,
1,Call,封装了一个RPC请求,包含了5个成员变量,分别是唯一标识符id,函数调用信息param,函数执行返回值value,出错或者异常信息(hadoop采用异步通信,远程过程调用的发生顺序与返回结果顺序无直接关系,而client正是通过id识别不同的函数调用的,所以当客户端发送请求时,只需要填充id和param两个变量,剩下的系统自动完成)
2,Connection,Client和每个Server之间维护一个通信连接,与该连接相关的基本信息(连接唯一标识ID,与Server通信的Socket,网络输出流,网络输入流,保存RPC的哈希表等)以及操作被封装到Connection类中
主要方法包括:,
1,addCall,将一个Call对象添加进hashmap
2,sendParam,向服务器发送RPC请求
3,eceiveResponse,从服务器接收已经处理完成的RPC请求
4,run,Connection是一个线程类,它的run方法会调用receiveResponse,会一直等待RPC返回结果
Server:服务器端
Server实现了典型的Reactor设计模式(事件驱动)
事件驱动设计模式的主要角色:
Reactor:IO事件的派发者
Acceptor:接受来自Client的连接,建立于Client对应的Handler,并向Reactor注册此Handler
Handler:与一个Client通信的实体,并按照一定的过程实现业务的处理。Handler内部往往会有更进一步的层次划分,用来抽象read,decode,compute,encode和send过程
Reader/Sender:为了加快处理速度,Reactor模式一般分离Handler中的读和写两个过程,分别注册成单独的读事件和写事件,并由对应的Reader和Sender线程处理
Server的工作流程大致被分为3个阶段:接收请求,处理请求,返回结果
1,接收请求
服务器采用唯一的Listener线程(内部包含了一个selector对象,用来监听OP_ACCEPT事件)来监听来自客户端的请求,一旦有新的请求达到,它会采用轮询的机制来线程池中选择一个Reader线程(内部包含了一个selector对象,用来监听OP_READ事件)进行处理(将请求封装成Call对象,放到共享队列中)
2,处理请求
Server端的Handler线程,他们并行的从共享队列中读取Call对象,经过执行对应的函数处理后,将尝试直接将结果返回给客户端,如果很难一次性发送到客户端,它会将尝试着将后续的发送任务交给Responder线程(同时会向该选择器注册一个OP_WRITE事件,来触发Responder线程)
3,返回结果:两种方式
1,Handler直接发送。
2,Responder线程(内部包含了一个selector对象,用来监听OP_WRITE事件)异步发送
一个完成的RPC通信步骤如下步骤:
1,创建connection对象,并将远程方法调用信息封装成Call对象,放到connection的hashMap中,
2,调用Connection的sendRpcRequest()方法将当前Call对象发送给Server端,
3,服务器接收请求,处理请求,并发送给Client端
4,Client通过receiveRpcResponse()函数获取结果
5,Client检查结果处理状态,将对应的call对象从hashmap中移除
Hadoop RPC参数调节
Reader线程数目,由ipc.server.read.threadpool.size指定,默认为1
每个Handler线程对应的最大Call数目,由ipc.server.handler.queuue.size指定,默认为100
Handler线程数目:RM和NN分别是YARN和HDFS两个子系统的RPC Server,分别由yarn.resourcemanager.resource-tracker.client.thread-count和
dfs.namenode.service.handler.count指定,默认值为50和10
客户端最大重试次数:由ipc.client.connect.max.retries指定,默认值为10
参考资料:董
|
|