提示:阅读本文前最好先阅读:
- 《Spark2.1.0之内置RPC框架》
- 《spark2.1.0之源码分析——RPC配置TransportConf》
- 《spark2.1.0之源码分析——RPC客户端工厂TransportClientFactory》
- 《spark2.1.0之源码分析——RPC服务器TransportServer》
- 《spark2.1.0之源码分析——RPC管道初始化》
- 《spark2.1.0之源码分析——RPC传输管道处理器详解》
- 《spark2.1.0之源码分析——服务端RPC处理器RpcHandler详解》
- 《spark2.1.0之源码分析——RPC服务端引导程序TransportServerBootstrap》
在《spark2.1.0之源码分析——服务端RPC处理器RpcHandler详解》一文曾介绍过服务端RpcHandler对请求消息的处理,现在来看看客户端发送RPC请求的原理。我们在分析《spark2.1.0之源码分析——RPC管道初始化》中列出的代码清单2中的createChannelHandler方法时,看到调用了TransportClient的构造器(见代码清单1),其中TransportResponseHandler的引用将赋给handler属性。
代码清单1 TransportClient的构造器
public TransportClient(Channel channel, TransportResponseHandler handler) {
this.channel = Preconditions.checkNotNull(channel);
this.handler = Preconditions.checkNotNull(handler);
this.timedOut = false;
}
TransportClient一共有五个方法用于发送请求,分别为:
- fetchChunk:从远端协商好的流中请求单个块;
- stream:使用流的ID,从远端获取流数据;
- sendRpc:向服务端发送RPC的请求,通过At least Once Delivery原则保证请求不会丢失;
- sendRpcSync:向服务端发送异步的RPC的请求,并根据指定的超时时间等待响应;
- send:向服务端发送RPC的请求,但是并不期望能获取响应,因而不能保证投递的可靠性;
本节只选择最常用的sendRpc和fetchChunk进行分析,其余实现都可以触类旁通。
发送RPC请求
sendRpc方法的实现见代码清单2。
代码清单2 sendRpc的实现
public long sendRpc(ByteBuffer message, final RpcResponseCallback callback) {
final long startTime = System.currentTimeMillis();
if (logger.isTraceEnabled()) {
logger.trace("Sending RPC to {}", getRemoteAddress(channel));
}
// 使用UUID生成请求主键requestId
final long requestId = Math.abs(UUID.randomUUID().getLeastSignificantBits());
handler.addRpcRequest(requestId, callback);// 添加requestId与RpcResponseCallback的引用之间的关系
// 发送RPC请求
channel.writeAndFlush(new RpcRequest(requestId, new NioManagedBuffer(message))).addListener(
new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
long timeTaken = System.currentTimeMillis() - startTime;
if (logger.isTraceEnabled()) {
logger.trace("Sending request {} to {} took {} ms", requestId,