文章目录
概述
Spark RPC之Netty启动 介绍了RpcEnv初始化过程中启动netty的过程,接下来我们查看启动后的netty如何提供rpc服务。
1. 基础概念
1.1 基础概念之ManagedBuffer
MangedBuffer是什么?
首先,对于Network通信,不管传输的是序列化后的对象还是文件,在网络上表现的都是字节流。
- 在传统IO中,字节流表示为Stream;
- 在NIO中,字节流表示为ByteBuffer;
- 在Netty中字节流表示为ByteBuff或FileRegion;
在Spark中,针对Byte也做了一层包装,支持对Byte和文件流进行处理,即ManagedBuffer;
在Netty中字节流表示为
ByteBuff
,spark将其封装为ManagedBuffer
,实现类如上下图,底层分别为java.io.File
、io.netty.buffer.ByteBuf
、java.nio.ByteBuffer
。
ManagedBuffer包含了三个函数来对Buffer进行“类型转换”:
createInputStream()
-> StreamnioByteBuffer()
-> ByteBufferconvertToNetty()
-> ByteBuff或FileRegionNioManagedBuffer / NettyManagedBuffer / FileSegmentManagedBuffer也是针对这ByteBuffer,ByteBuff或FileRegion提供了具体的实现。
更好的理解ManagedBuffer:
- 比如Shuffle BlockManager模块需要在内存中维护
本地executor生成的shuffle-map输出的文件引用
,从而可以提供给shuffleFetch进行远程读取,此时文件表示为FileSegmentManagedBuffer
shuffleFetch
远程调用FileSegmentManagedBuffer.nioByteBuffer
/createInputStream
函数从文件中读取为Bytes
,并进行后面的网络传输。- 如果
已经在内存中bytes
就更好理解了,比如将一个字符数组表示为NettyManagedBuffer
。
1.2 基础概念之Message
spark对消息的类型进行了分类,主要为ChunkFetch
、Rpc
和Stream
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
:- 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
:- RPC是第三种核心的Message.
- 和Stream、ChunkFetch的Message不同,每次通信的Body是类型是确定的,在rpcHandler可以根据每种Body的类型进行相应的处理。
1.3 基础概念之TransportChannelHandler(Netty处理RPC请求依赖)
Netty处理RPC类型请求依赖TransportChannelHandler,TransportChannelHandler继承SimpleChannelInboundHandler,在TransportServer初始化时添加到pipeline中,关于Channel、ChannelHandler和pipeline的概念和使用请参考Netty User guide for 4.x。
UML类图如下:
2. 处理RpcRequest请求
2.1 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部分。
2.2 RpcEndpointRef和RpcEndpoint在一台机器
在同一台机器时,不需要netty,直接访问RpcEndpoint,如上图,依然交给Dispatcher、Inbox处理。
总结
介绍netty相关的基础概念ManagedBuffer、Message、TransportChannelHandler,及处理RpcRequest请求的两种情形。