文章目录
- 一、创建TransportContext及其内部成员分析
- 二、使用TransportConxt创建TransportServer
- 三、使用TransportConxt创建TransportClientFactory多客户端连接池
- 四、再次回顾TransportContext方法:initializePipeline()及createChannelHandler()及消息链路
- 五、在项目中的应用案例
Netty 是一个基于NIO的客户、服务器端编程框架,Netty 是一个基于NIO的客户、服务器端编程框架。
Spark Netty RPC通信可分为两大部分:
- 首先,是Spark 对Netty通信基础Api的封装,实现Server和Client,这部分代码主要以java实现,在network-common 子项目中,分别实现了TransportServer、TransportClientFactory(连接池)、TransportClient(客户端)等功能
- 后续,在下一篇文章,我们会分析Rpc消息如何在应用层创建、发送、分发(dispatcher)、处理(Endpoint)及回调,这一完整流程
在本文,我们主要介绍Spark对Netty通信层的封装,以及在参考Spark Netty实现原理的基础上,在项目中的应用。
Netty通信基础框架中,NettyRpcEnv初始化创建TransportContext,其具有createServer()、createClientFactory()核心功能,是创建服务端和客户端连接池的起点。
以下TransportContex概要图在查看过程中,注意有①~④子模块,需各自对应起来分析。
一、创建TransportContext及其内部成员分析
在NettyRpcEnv中创建TransportContext:
private[netty] class NettyRpcEnv(
val conf: SparkConf,
javaSerializerInstance: JavaSerializerInstance,
host: String,
...) extends RpcEnv(conf) with Logging {
...
val transportConf = SparkTransportConf.fromSparkConf(
conf.clone.set("spark.rpc.io.numConnectionsPerPeer", "1"),
"rpc",
conf.getInt("spark.rpc.io.threads", numUsableCores))
val streamManager = new NettyStreamManager(this)
// 创建TransportContext上下文
private val transportContext = new TransportContext(transportConf,
new NettyRpcHandler(dispatcher, this, streamManager))
...
}
1、TransportContext内部成员分析:TransportConf上下文配置
从NettyRpcEnv中创建transportConf可知,其数据源是SparkConf全局配单的一份拷贝,同时提供了netty相关配置的get()方法,包括io mode、timeout、buffer size、threads等netty配置:
public class TransportConf {
private final ConfigProvider conf;
...
public int numConnectionsPerPeer() {
return conf.getInt(SPARK_NETWORK_IO_NUMCONNECTIONSPERPEER_KEY, 1);
}
public int backLog() { return conf.getInt(SPARK_NETWORK_IO_BACKLOG_KEY, -1); }
public int serverThreads() { return conf.getInt(SPARK_NETWORK_IO_SERVERTHREADS_KEY, 0); }
public int clientThreads() { return conf.getInt(SPARK_NETWORK_IO_CLIENTTHREADS_KEY, 0); }
public int receiveBuf() { return conf.getInt(SPARK_NETWORK_IO_RECEIVEBUFFER_KEY, -1); }
}
2、TransportContext内部成员分析:NettyRpcHandler
由TransportContext的创建可知,RpcHandler的实现为: new NettyRpcHandler(dispatcher, this, streamManager)
NettyRpcHandler内部具有dispatcher和streamManager两个重要成员,这里分别介绍:
I、NettyRpcHandler:dispatcher消息分发
dispatcher作用是在server端分发各rpc消息至inbox和应用级Endpoint,具体分发流程在下一篇文章介绍,这里只做简单介绍,NettyRpcHandler中,涉及dispatcher有两类用法:
A、接收数据:
** receive(client, messages, callback)【server TransportRequestHandler#processRpcRequest()处理Rpc消息】、
** receive(client, messages)【server TransportRequestHandler#processOneWayMessage()处理单向消息】、
** internalReceive()【直接数据包装为RequestMessage】
B、代理TransportRequestHandler的channelActive()、channelInactive()、exceptionCaught()方法,在channel通道连接、失去连接、捕获异常时,使用postToAll()给dispatcher发送对应远端client状态消息
II、NettyRpcHandler:streamManager文件下载消息流程示例
- NettystreamManager作用是文件传输,我们通过SparkContex调用addJar()增加依赖jar包,会调用env.rpcEnv.fileServer.addJar(file),将文件添加到NettyStreamManager中
- OneForOneStreamManger用于点对点chunk传输,核心方法是getChunk(),其创建流程是NettyBlockTransferService–>NettyBlockRpcServer–>OneForOneStreamManager,最后nettyBlockTransferService在SparkEnv中由BlockManager持有,提供块传输服务(文章篇幅限制,这个分支感兴趣的读者可以深入研究)。
以NettystreamManager为例,介绍下载文件的整体流程:
A、遍历需要下载的文件url,发起StreamRequest请求
NettyStreamManager的核心方法是openStream(streamId),将文件打开为输出流。
由Executor发起请求文件的StreamRequest消息,并最终返回文件流及StreamResponse:
上图需要注意的点:
- TransportRequestHandler是在Server(即spark driver)中,进行文件的打开、返回消息的封装,并通过channel写出。
- 其余模块均在Executor中,属于Client端功能。
B、StreamResponse在TransportResponseHandler和Executor端处理
以TransportResponseHandler中handler(responseMessage)为起点,Executor中处理StreamResponse消息的流程如下图
分为查找callback、设置拦截器、在TransportFrameDecoder的channelRead()中调用拦截器的handler()方法处理数据。
二、使用TransportConxt创建TransportServer
TransportServer是一个逻辑上的服务端概念,init()方法内部调用contex.initializePipleline()时,创建了TransportChannelHandler、TransportRequestHandler,与指定的channel绑定,用于channelRead()时消息读取,最后将消息交给其内部nettyRpcHandler的dispatcher进行消息分发,最后,各应用Endpoint会接收和最终响应消息。
public class TransportContext {
public TransportServer createServer(int port, List<TransportServerBootstrap> bootstraps) {
return new TransportServer(this, null, port, rpcHandler, bootstraps);