Client端比Server端简单,只有两种线程:
- Client线程:发起RPC的主线程
- Connection线程:每一个ConnectionId (由<remoteAddress, protocol, ticket>组成)唯一对应一个Connection线程,用以接受server端的response
如上图所示,基本的工作流程如下:
- Client线程将输入参数值封装成Call,Call在原来的基础上增加了RPC完成标志和RPC返回值信息;随后,创建一个Connection
- 用ConnectionId作为key,将新建的Connection放入Client的Connection Map,以callId作为key,将之前的Call放入Connection的Call Map
- 通过socket建立连接,并发送RPC Header和Protocal Header
- 启动Connection线程
- Connection线程监听socket,等待server response;同时,Client线程发送RPC Parameters
- Client等待Call返回,call.wait()
- Connection线程收到response,唤醒Client线程读取Call返回值,call.notify()
与server建立连接是比较expensive的操作,Connection Map实际上是一个Connection Pool,用于复用之前创建的Connection。在复用Connection的情况下,工作流程有所不同。
如上图所示,复用Conneciton的工作流程如下:
- 之前创建的Connection在完成上次的任务后,如果Call Map为空,将开始wait直到timeout
- Client线程将输入参数值封装成Call;随后,从Connection Map中取出现有Connection
- 以以callId作为key,将之前的Call放入Connection的Call Map,接着notify此Connection线程
- Connection线程被唤醒后,开始监听socket,等待server response;同时,Client线程发送RPC Parameters
- Client等待Call返回,call.wait()
- Connection线程收到response,唤醒Client线程读取Call返回值,call.notify()
可以看到,复用Connection,不需要重新建立连接和发送RPC Header及Protocal Header。当然,Connection Map中的Connection不会一直保持连接,在timeout时间后如果Call Map还是为空,那么Connection将会关闭连接,并将自己从Connection Map中移除。
Code-level
- org.apache.hadoop.ipc.Client.call(Writable, ConnectionId)
- org.apache.hadoop.ipc.Client.getConnection(ConnectionId, Call)
- org.apache.hadoop.ipc.Client.Connection.setupIOstreams()
- org.apache.hadoop.ipc.Client.Connection.receiveResponse()
- org.apache.hadoop.ipc.Client.Connection.sendParam(Call)