在前面源码剖析介绍中,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. 从零开始学习 asp.net core 2.1 web api 后端api基础框架(一)-环境介绍

    原文:从零开始学习 asp.net core 2.1 web api 后端api基础框架(一)-环境介绍 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.ne ...

  2. uboot初体验-----趣谈nand设备发起的浅显理解

    1 选择Uboot版本号 2 移植uboot至console正常work 3 制造uImage和使用uboot指南 4 写NFC驱动器 5 uboot从nand启动引导系统 1 选择Uboot版本号 ...

  3. 经典c开源项目

    1. Webbench Webbench是一个在Linux下使用的非常简单的网站压测工具.它使用fork()模拟多个客户端同时访问我们设定的URL,测试网站在压力下工作的性能,最多可以模拟3万个并发连 ...

  4. WPF Datagrid with some read-only rows - Stack Overflow

    原文:WPF Datagrid with some read-only rows - Stack Overflow up vote 21 down vote accepted I had the sa ...

  5. 华为开发者论坛FusionStage版块

    FusionStage版块 http://developer.huawei.com/ict/forum/forum.php?mod=forumdisplay&fid=400191&pa ...

  6. MEF 插件式开发 - WPF 初体验

    原文:MEF 插件式开发 - WPF 初体验 目录 MEF 在 WPF 中的简单应用 加载插件 获取元数据 依赖注入 总结 MEF 在 WPF 中的简单应用 MEF 的开发模式主要适用于插件化的业务场 ...

  7. 利用WIX制作安装包(3)

    原文 利用WIX制作安装包(3) 利用WIX安装服务非常简单.只需要短短几句话就可以.当我们创建好一个Windows服务之后.我们在项目中创建一个Service.wxs 文件来安装服务,并且编辑代码如 ...

  8. C#连接oracle 数据库查询时输入中文查询不出来,用plsql就可以

    查询语句为:select * from Per where khmc like '%李%',其实是字符集的问题. 解决方案:在连接字符串加一个“Unicode=True;”

  9. So Good They Can't Ignore You

    总体而言,这本书的作者的观点就是,你只有做好了,才会有兴趣,而不是一开始就找可能并不存在的所谓兴趣——好多人就败在不停地找这么一个根本就不存在的兴趣.这个观点简直就是拯救那些乔布斯的粉丝:Follow ...

  10. mysql多对一、多对多查询实践

    最近做的功能涉及这方面,算是练了一下. 首先多对一部分较简单.多的那一方表,多设置一个字段是少的那一方的id(主键). 具体查询时候关联查询即可. 可设置外键进行级联操作. 具体以后做到这个功能再更新 ...