在前面源码剖析介绍中,spark 源码分析之二 -- SparkContext 的初始化过程 中的SparkEnv和 spark 源码分析之四 -- TaskScheduler的创建和启动过程 中的ClientApp启动过程中,都涉及到了Spark的内置RPC的知识。本篇专门把RPC 拿出来剖析一下。

因为RPC 在 Spark 中内容虽然不多,但理清楚还是花费很多精力的,计划每天只剖析一小部分,等剖析完毕,会专门有一篇总结性的文章出来。

本篇作为RPC分析开篇,主要剖析了NettyRpcEnv创建的过程。

Spark Rpc使用示例

我们以 org.apache.spark.deploy.ClientApp#start 方法中的调用API创建 RPC 的过程入口。

// 1. 创建 RPC Environment
val rpcEnv = RpcEnv.create("driverClient", Utils.localHostName(), 0, conf, new SecurityManager(conf))

创建NettyRpcEnv

如下是创建NettyRpcEnv的时序图(画的不好看,见谅):

RpcEnv是scala 的object伴生对象(本质上是一个java 单例对象),去调用NettyRpcEnvFactory去创建 NettyRpcEnv 对象,序列化使用的是java序列化内建的方式,然后调用Utils 类重试启动Server。启动成功后返回给用户。

org.apache.spark.rpc.netty.NettyRpcEnv#startServer 代码如下:

 def startServer(bindAddress: String, port: Int): Unit = {
val bootstraps: java.util.List[TransportServerBootstrap] =
if (securityManager.isAuthenticationEnabled()) {
java.util.Arrays.asList(new AuthServerBootstrap(transportConf, securityManager))
} else {
java.util.Collections.emptyList()
}
server = transportContext.createServer(bindAddress, port, bootstraps)
dispatcher.registerRpcEndpoint(
RpcEndpointVerifier.NAME, new RpcEndpointVerifier(this, dispatcher))
}

在TransportServer构造过程中调用了init方法。org.apache.spark.network.server.TransportServer#init 源码如下:

 private void init(String hostToBind, int portToBind) {

   IOMode ioMode = IOMode.valueOf(conf.ioMode());
EventLoopGroup bossGroup =
NettyUtils.createEventLoop(ioMode, conf.serverThreads(), conf.getModuleName() + "-server");
EventLoopGroup workerGroup = bossGroup; PooledByteBufAllocator allocator = NettyUtils.createPooledByteBufAllocator(
conf.preferDirectBufs(), true /* allowCache */, conf.serverThreads()); bootstrap = new ServerBootstrap()
.group(bossGroup, workerGroup)
.channel(NettyUtils.getServerChannelClass(ioMode))
.option(ChannelOption.ALLOCATOR, allocator)
.option(ChannelOption.SO_REUSEADDR, !SystemUtils.IS_OS_WINDOWS)
.childOption(ChannelOption.ALLOCATOR, allocator); this.metrics = new NettyMemoryMetrics(
allocator, conf.getModuleName() + "-server", conf); if (conf.backLog() > 0) {
bootstrap.option(ChannelOption.SO_BACKLOG, conf.backLog());
} if (conf.receiveBuf() > 0) {
bootstrap.childOption(ChannelOption.SO_RCVBUF, conf.receiveBuf());
} if (conf.sendBuf() > 0) {
bootstrap.childOption(ChannelOption.SO_SNDBUF, conf.sendBuf());
} bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
RpcHandler rpcHandler = appRpcHandler;
for (TransportServerBootstrap bootstrap : bootstraps) {
rpcHandler = bootstrap.doBootstrap(ch, rpcHandler);
}
context.initializePipeline(ch, rpcHandler);
}
}); InetSocketAddress address = hostToBind == null ?
new InetSocketAddress(portToBind): new InetSocketAddress(hostToBind, portToBind);
channelFuture = bootstrap.bind(address);
channelFuture.syncUninterruptibly(); port = ((InetSocketAddress) channelFuture.channel().localAddress()).getPort();
logger.debug("Shuffle server started on port: {}", port);
}

主要功能是:调用netty API 初始化 nettyServer。

org.apache.spark.rpc.netty.Dispatcher#registerRpcEndpoint的源码如下:

 def registerRpcEndpoint(name: String, endpoint: RpcEndpoint): NettyRpcEndpointRef = {
val addr = RpcEndpointAddress(nettyEnv.address, name)
val endpointRef = new NettyRpcEndpointRef(nettyEnv.conf, addr, nettyEnv)
synchronized {
if (stopped) {
throw new IllegalStateException("RpcEnv has been stopped")
}
if (endpoints.putIfAbsent(name, new EndpointData(name, endpoint, endpointRef)) != null) {
throw new IllegalArgumentException(s"There is already an RpcEndpoint called $name")
}
val data = endpoints.get(name)
endpointRefs.put(data.endpoint, data.ref)
receivers.offer(data) // for the OnStart message
}
endpointRef
}

EndpointData 在初始化过程中会放入 OnStart 消息。
在 Inbox 的 process 中,有如下代码:

 case OnStart =>
endpoint.onStart()
if (!endpoint.isInstanceOf[ThreadSafeRpcEndpoint]) {
inbox.synchronized {
if (!stopped) {
enableConcurrent = true
}
}
}

调用 endpoint 的 onStart 方法和 初始化 是否支持并发处理模式。endpoint 指的是 RpcEndpointVerifier, 其 onStart 方法如下:

 /**
* Invoked before [[RpcEndpoint]] starts to handle any message.
*/
def onStart(): Unit = {
// By default, do nothing.
}

即不做任何事情,直接返回,至此初始化NettyRPCEnv 流程就剖析完。伴生对象RpcEnv调用netty rpc 工厂创建NettyRpcEnv 对象,然后使用重试机制启动TransportServer,然后NettyRpcEnv注册RpcEndpointVerifier

到Dispatcher。最终返回 NettyRpcEnv 给API调用端,NettyRpcEnv 创建成功。在这里,Dispatcher 和 TransportServer 等组件暂不做深入了解,后续会一一剖析。

spark 源码分析之五 -- Spark内置RPC机制剖析之一创建NettyRpcEnv的更多相关文章

  1. spark 源码分析之十二 -- Spark内置RPC机制剖析之八Spark RPC总结

    在spark 源码分析之五 -- Spark内置RPC机制剖析之一创建NettyRpcEnv中,剖析了NettyRpcEnv的创建过程. Dispatcher.NettyStreamManager.T ...

  2. spark 源码分析之六--Spark RPC剖析之Dispatcher和Inbox、Outbox剖析

    在上篇 spark 源码分析之五 -- Spark内置RPC机制剖析之一创建NettyRPCEnv 中,涉及到了Diapatcher 内容,未做过多的剖析.本篇来剖析一下它的工作原理. Dispatc ...

  3. Spark源码分析之Spark Shell(下)

    继上次的Spark-shell脚本源码分析,还剩下后面半段.由于上次涉及了不少shell的基本内容,因此就把trap和stty放在这篇来讲述. 上篇回顾:Spark源码分析之Spark Shell(上 ...

  4. spark 源码分析之八--Spark RPC剖析之TransportContext和TransportClientFactory剖析

    spark 源码分析之八--Spark RPC剖析之TransportContext和TransportClientFactory剖析 TransportContext 首先官方文档对Transpor ...

  5. Spark源码分析之五:Task调度(一)

    在前四篇博文中,我们分析了Job提交运行总流程的第一阶段Stage划分与提交,它又被细化为三个分阶段: 1.Job的调度模型与运行反馈: 2.Stage划分: 3.Stage提交:对应TaskSet的 ...

  6. Spark源码分析之Spark Shell(上)

    终于开始看Spark源码了,先从最常用的spark-shell脚本开始吧.不要觉得一个启动脚本有什么东东,其实里面还是有很多知识点的.另外,从启动脚本入手,是寻找代码入口最简单的方法,很多开源框架,其 ...

  7. spark 源码分析之七--Spark RPC剖析之RpcEndPoint和RpcEndPointRef剖析

    RpcEndpoint 文档对RpcEndpoint的解释:An end point for the RPC that defines what functions to trigger given ...

  8. Spark 源码分析系列

    如下,是 spark 源码分析系列的一些文章汇总,持续更新中...... Spark RPC spark 源码分析之五--Spark RPC剖析之创建NettyRpcEnv spark 源码分析之六- ...

  9. spark 源码分析之十一--Spark RPC剖析之TransportClient、TransportServer剖析

    TransportClient类说明 先来看,官方文档给出的说明: Client for fetching consecutive chunks of a pre-negotiated stream. ...

随机推荐

  1. WPF 图片灰度处理

    原文:WPF 图片灰度处理 文章的内容是来自微软中文技术论坛的一个帖子,当时是想将一段将图片灰度处理的代码转换为XAML的一个样式,在这里要谢谢 Xiao Yan Qiang.Sheldon _Xia ...

  2. MySQL第五个学习笔记 该数据表的操作

    MySQL在创建表,创建.frm文件保存表和列定义.索引存储在一个.MYI(MYindex)且数据存储在有.MYD(MYData)扩展名的文件里.   一.用SHOW/ DESCRIBE语句显示数据表 ...

  3. Android开发四大件

    四大组件 Activity Activity是Android应用程序的界面,比如查看联系人.打电话.玩游戏的界面等一个应用程序通常包含多个Activity,即多个界面Activity通过布局管理各种V ...

  4. QT Linux Demo程序编译

    我手上的qt源码包为:qt-everywhere-opensource-src-4.7.0.tar.gz 在Linux下编译比较容易,解压后直接 ./configure,一般会报缺少什么库这些.自己遇 ...

  5. delphi中获取memo鼠标所在位置的行和列(通过EM_GETRECT消息取得Rect后,自己算一下)

    也是看别人写的,但是不容易找到,就转发一篇delphi版本的 function GetLine(X, Y: integer): TPoint;var  OldFont : HFont;  Hand : ...

  6. WPF/Silverlight 页面绑定Model验证机制升级版

    关于WPF/Silverlight的数据验证,想必大家都不陌生了. 各大牛的博客里都不泛对这方面讨论的文章. 个人比较赞赏 JV9的“Silverlight实例教程”里的Validation数据验证. ...

  7. linux自动挂载远程网盘到本地

    sudo vim /etc/fstab  添加如下内容 //192.168.1.110/MyFiles /path/to/mount cifs username=adminz,password=pas ...

  8. 【备忘】C#语言基础-1

    C#基础 注意区别哟! C#是一种托管语言,与C++不同,不能直接操作系统底层,依赖于 framework. 如果局部变量和全局变量同名,全局变量就会被屏蔽. 函数的返回值类型不是签名的一部分,所以不 ...

  9. ThinkPHP 提供Auth 权限管理、支付宝、微信支付、阿里oss、友盟推送、融云即时通讯、云通讯短信、Email、Excel、PDF 等等

    多功能 THinkPHP 开源框架 项目简介:使用 THinkPHP 开发项目的过程中把一些常用的功能或者第三方 sdk 整合好,开源供亲们参考,如 Auth 权限管理.支付宝.微信支付.阿里oss. ...

  10. Linux下C/C++帮助手册安装方法

    1.  安装C的帮助手册 如果你使用的Linux发行版, 默认没有安装C语言的库函数MAN手册, 使用下面的方法解决: # sudo apt-get install manpages # sudo a ...