概要
Spark RPC之Netty启动 介绍了RpcEnv初始化过程中启动netty的过程,接下来我们查看启动后的netty如何提供rpc服务。
基础概念
先了解些基础概念,和netty相关。
ManagedBuffer
对于Network通信,不管传输的是序列化后的对象还是文件,在网络上表现的都是字节流。在Netty中字节流表示为ByteBuff,spark将其封装为ManagedBuffer,实现类如上图,底层分别为java.io.File、io.netty.buffer.ByteBuf、java.nio.ByteBuffer。
Message
spark对消息的类型进行了分类,主要为ChunkFetch、Rpc和Stream。
Stream消息就是ManagedBuffer中的ByteBuff,在Spark内部,比如SparkContext.addFile操作会在Driver中针对每一个add进来的file/jar会分配唯一的StreamId(file/[]filename],jars/[filename]);worker通过该StreamId向Driver发起一个StreamRequest的请求,Driver将文件转换为FileSegmentManagedBuffer返回给Worker,这就是StreamMessage的用途之一;
ChunkFetch也有一个类似Stream的概念,ChunkFetch的对象是“一个内存中的Iterator[ManagedBuffer]”,即一组Buffer,每一个Buffer对应一个chunkIndex,整个Iterator[ManagedBuffer]由一个StreamId标识。Client每次的ChunkFetch请求是由(streamId,chunkIndex)组成的唯一的StreamChunkId,Server端根据StreamChunkId获取为一个Buffer并返回给Client; 不管是Stream还是ChunkFetch,在Server的内存中都需要管理一组由StreamId与资源之间映射,即StreamManager类,它提供了getChunk和openStream两个接口来分别响应ChunkFetch与Stream两种操作,并且针对Server的ChunkFetch提供一个registerStream接口来注册一组Buffer,比如可以将BlockManager中一组BlockID对应的Iterator[ManagedBuffer]注册到StreamManager,从而支持远程Block Fetch操作。
RPC是第三种核心的Message,和Stream、ChunkFetch的Message不同,每次通信的Body是类型是确定的,在rpcHandler可以根据每种Body的类型进行相应的处理。
TransportChannelHandler
Netty处理RPC类型请求依赖TransportChannelHandler,,TransportChannelHandler继承SimpleChannelInboundHandler,在TransportServer初始化时添加到pipeline中,关于Channel、ChannelHandler和pipeline的概念和使用请参考Netty User guide for 4.x。
UML类图如下
处理RpcRequest请求
RpcEndpointRef和RpcEndpoint不在一台机器
不在同一台机器时,需要借助于netty,大致步骤如下
- 如Spark RPC之Netty启动 所述,创建RpcEnv时启动netty server,同时将TransportChannelHandler添加到pipeline中。
- 如上图,TransportChannelHandler处理netty接收到的数据,依次交给TransportRequestHandler、NettyRpcHandler处理。
- 最后交由Dispatcher、Inbox,请参考Spark RPC之Dispatcher、Inbox、Outbox 。
上图给出了netty内部如何处理数据的流程,对应的client如何发送数据给netty server参考Spark RPC之Dispatcher、Inbox、Outbox 中的Outbox部分。
RpcEndpointRef和RpcEndpoint在一台机器
在同一台机器时,不需要netty,直接访问RpcEndpoint,如上图,依然交给Dispatcher、Inbox处理。
总结
介绍netty相关的基础概念ManagedBuffer、Message、TransportChannelHandler,及处理RpcRequest请求的两种情形。