背景
Spark中有很多异步处理的例子,每一个地方都值得好好去审视一番,对辅助理解spark的机理以及为自己写出优雅的代码都会有很大的帮助。
NettyRpcEnv.ask解读
RpcEnv作用
NettyRpcEnv
是RpcEnv
的在spark中的唯一一个实现。RpcEnv
是什么呢,可以先看一下它的class头信息
/**
* An RPC environment. [[RpcEndpoint]]s need to register itself with a name to [[RpcEnv]] to
* receives messages. Then [[RpcEnv]] will process messages sent from [[RpcEndpointRef]] or remote
* nodes, and deliver them to corresponding [[RpcEndpoint]]s. For uncaught exceptions caught by
* [[RpcEnv]], [[RpcEnv]] will use [[RpcCallContext.sendFailure]] to send exceptions back to the
* sender, or logging them if no such sender or `NotSerializableException`.
*
* [[RpcEnv]] also provides some methods to retrieve [[RpcEndpointRef]]s given name or uri.
*/
就是一句话,RPC的环境。在这里,最重要的2个操作莫过于
- 可以去注册
RpcEndpoint
- 可以去异步获取
RpcEndpointRef
而RpcEndpoint
和RpcEndpointRef
是什么呢,在这里不做详细赘述,其他的文章中会详细说明,简单来讲一下
简单回顾RpcEndpoint
和RpcEndpointRef
RpcEndpoint
-
RpcEndpoint
众所周知,spark内部会有
executor
,driver
等角色,他们之间的通信都采用利用Netty,在executor或者driver上并不是只启动1个Netty的服务,针对不同的功能会有多个Netty的RPC服务开启,利用不同的端口号进行区分。服务间通信后,通的“信”被很多种逻辑单元来处理,如Inbox
,如EventLoop
等,这些都是工具级别的单元,而被抽象出来作为可插拔可扩展的大的逻辑功能模块在Spark中就叫做RpcEndpoint
,它是用来处理从其他client
端发送或者server
端返回过来的message
的模块。RpcEndpoint
本身是一个trait,它可以有多种的实现
RpcEndpointRef
-
RpcEndpointRef
spark之前的网络通信都是采用akka,改版后采用的是Netty,在akka中,如果一个两个节点间的通信是利用目的方的actorRef来进行的通信的,即AActor 希望发送消息到 BActor,需要BActorRef来发送消息。Spark的网络通信升级到Netty后,Endpoint就可以间接理解成原来的Actor,那么发送消息到另一个Actor的话,也需要
RpcEndpoint
的Ref,即RpcEndpointRef
。这个概念乍一看有点懵,试想,从A发送消息到B,能发送的前提是A先拥有了一个B的”引用“,这在普通的Http服务中貌似很不能被理解,我想访问某一台机器按说只需要知道对方的IP和Port不就OK了,现在还需要对方的一个“替身”?这是什么鬼?带着问题我们可以持续往下看即可,这里你只需要这样意识即可:- 用来访问B machine的
RpcEndpointRef
你理解成就是B machine的IP和Port的一个被包装后的实例即可
- 用来访问B machine的
图解RpcEndpoint
和RpcEndpointRef
-
图解一下
A machine可以是物理机可以是虚拟机,B machine可以是和A同一台物理机、虚拟机(端口号不同),也可以是不同的(在spark中甚至于有自己发给自己的msg,后续会讲)。那么从A发送消息到B的话,使用的是B的
RpcEndpointRef
,通过它发送消息到B machine- 【图1】要如何访问
* 【图2】内部的原理
* 【图3】B machine的RpcEndpointRef的实例是啥(简化版)
简单回顾Driver和Executor
Ask,顾名思义——问。可能是打个招呼,看看在不在,询问一下,等等。这个就是NettyRpcEnv.ask的作用所在。为了讲NettyRpcEnv.ask的作用,还需要简单的串一下一下概念和流程
Driver线程和Executor进程
首先,需要明确两个事情,在yarn环境下
-
Driver
是在ApplicatioMaster
进程中执行的一个线程严格来说,其实这个说法也不太正确,因为
Driver
其实是在用户的class的时候,在形成sparkContext
上下文环境的一个产物,本身执行的其实是用户class线程,在这个线程中建立了SparkEnv
以及RpcEnv
等等,并且建立了Driver
的Netty的Service等等,与Executor
相互通信 -
Executor
则是一个个的进程,通过java命令在每一个节点上启动的
Yarn系列以及 ApplicatioMaster
是什么这里不做赘述,其他文章中会细讲。
其次,在这里只需要了解到,Driver
本身是一个协调调度节点,它可以去分配任务给Executor
,并且掌握着Executor
的情况,分配就是把Task发送给