spark 源码分析之五 -- Spark内置RPC机制剖析之一创建NettyRpcEnv
在前面源码剖析介绍中,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的更多相关文章
- spark 源码分析之十二 -- Spark内置RPC机制剖析之八Spark RPC总结
在spark 源码分析之五 -- Spark内置RPC机制剖析之一创建NettyRpcEnv中,剖析了NettyRpcEnv的创建过程. Dispatcher.NettyStreamManager.T ...
- spark 源码分析之六--Spark RPC剖析之Dispatcher和Inbox、Outbox剖析
在上篇 spark 源码分析之五 -- Spark内置RPC机制剖析之一创建NettyRPCEnv 中,涉及到了Diapatcher 内容,未做过多的剖析.本篇来剖析一下它的工作原理. Dispatc ...
- Spark源码分析之Spark Shell(下)
继上次的Spark-shell脚本源码分析,还剩下后面半段.由于上次涉及了不少shell的基本内容,因此就把trap和stty放在这篇来讲述. 上篇回顾:Spark源码分析之Spark Shell(上 ...
- spark 源码分析之八--Spark RPC剖析之TransportContext和TransportClientFactory剖析
spark 源码分析之八--Spark RPC剖析之TransportContext和TransportClientFactory剖析 TransportContext 首先官方文档对Transpor ...
- Spark源码分析之五:Task调度(一)
在前四篇博文中,我们分析了Job提交运行总流程的第一阶段Stage划分与提交,它又被细化为三个分阶段: 1.Job的调度模型与运行反馈: 2.Stage划分: 3.Stage提交:对应TaskSet的 ...
- Spark源码分析之Spark Shell(上)
终于开始看Spark源码了,先从最常用的spark-shell脚本开始吧.不要觉得一个启动脚本有什么东东,其实里面还是有很多知识点的.另外,从启动脚本入手,是寻找代码入口最简单的方法,很多开源框架,其 ...
- spark 源码分析之七--Spark RPC剖析之RpcEndPoint和RpcEndPointRef剖析
RpcEndpoint 文档对RpcEndpoint的解释:An end point for the RPC that defines what functions to trigger given ...
- Spark 源码分析系列
如下,是 spark 源码分析系列的一些文章汇总,持续更新中...... Spark RPC spark 源码分析之五--Spark RPC剖析之创建NettyRpcEnv spark 源码分析之六- ...
- spark 源码分析之十一--Spark RPC剖析之TransportClient、TransportServer剖析
TransportClient类说明 先来看,官方文档给出的说明: Client for fetching consecutive chunks of a pre-negotiated stream. ...
随机推荐
- CSS position财产
CSS在position位置信息要素用于表示属性. 有三个起飞值:static, absolute, relative. 假设元件不显式配置position财产,该元素默认position 值至sta ...
- 前端PS常用切图技巧
前言:前端涉及到的 ps 操作不算复杂,基本上就是切图,本文总结了常用的几种切图技巧. 工具:photoshop cs6 . photoshop cc 1. 传统切图 01 这是最笨的一种方法,核心就 ...
- 【全面解禁!真正的Expression Blend实战开发技巧】第八章 FluidMoveBehavior完全解析之一漂浮移动
原文:[全面解禁!真正的Expression Blend实战开发技巧]第八章 FluidMoveBehavior完全解析之一漂浮移动 好久没更新博客了,今天如果没急事,准备连发三篇,完全讲解Blend ...
- C++调用Python浅析
环境 VS2005Python2.5.4 Windows XP SP3 简述 一般开发过游戏的都知道Lua和C++可以很好的结合在一起,取长补短,把Lua脚本当成类似动态链接库来使用,很好的利用了脚本 ...
- C# XML 去xmlns:xsd和xmlns:xsi属性
public static XElement WithoutNamespaces(this XElement element) { if (element == null) return null; ...
- Android手机导出文件
因为要写联系人相关的东西,所以得把db导出来看看 第一步:Root手机 尝试了几个Root工具,发现就KingRoot能root 第二个:编写bat脚本 脚本内容是先将DB文件从/data/data ...
- 在Delphi中静态调用DLL
在Delphi中静态调用DLL top 调用一个DLL比写一个DLL要容易一些.首先给大家介绍的是静态调用方法,稍后将介绍动态调用方法,并就两种方法做一个比较.同样的,我们先举一个静态调用的例子. u ...
- /\B(?=(?:\d{3})+$)/g 一条令人费解的正则表达式
网上浏览博客看到要用JavaScript正则表达式解决一个功能, 要在数字中间插入逗号, 用来表示书面的金额写法. JS代码是这样子的 let test1 = '1234567890' let for ...
- 我写的一个Qt 显示二维码( QR Code)的控件(可以去掉对 libpthread 的依赖,而且编译出的库文件可以在 vc2010 的release 模式下使用)
最近一个项目需要显示二维码,所以花了点时间(只用了一个晚上,写的很不完善),写了个显示二维码的控件.当然这个控件用到了些开源的代码,比如qrencode,所以我也打算把我的代码开源. 我的代码参考了 ...
- GetParent、SetParent、MoveWindow - 获取、指定父窗口和移动窗口,IsChild - 判断两个窗口是不是父子关系
提示: SetParent 应该 Windows.SetParent, 因为 TForm 的父类有同名方法. //声明: {获取父窗口句柄} GetParent(hWnd: HWND): HWND; ...