大画 Spark :: 网络 (5)-Spark 中的 server 端和 client 端
回顾
上一篇介绍了 Endpoint 的构建流程,采用sparkEnv
→ NettyRpcEnv
→ Dispatcher
的构建顺序。并且探讨了最基础的 Driver 和 Executor 的职责与关系
本篇主旨
介绍在 Drvier 端是如何构建起 Netty 的 server 服务,以及在 client 端又是如何构建起 client 服务的过程
可能会涉及到一些Netty
的知识,我会穿插做基础知识的介绍。
整体的构建结构
Driver(server)和 Executor(client)双方的构建方式基本相同,结构如下
构建源的区别
Driver
Driver 的构建采用的是从sparkContext
的开始进行构建,它可以理解成是用户程序与 spark 集群的 entry point(这个是源代码中的 class 头注释),用户程序与 spark 进行交互时,都要先声明sparkContext
,读取数据的操作包括 RDD 也都是从sparkContext
开始一步步的衍生而来,这些概念后续聊。
Executor
Executor 的构建,是从一个叫做CoarseGrainedExecutorBackend
的 class 中开始的,这个类是 Executor 在某一台服务器上执行的进程 class,可以在这个类中看到main
方法。
构建 server 和 client
上一篇文章说过,Driver 端充当的是 server 的角色,Executor 充当的是 client 的角色。那么在整个启动和构建环境的过程中也会存在些许差异。
Driver 端构建过程中会启动一个基于Netty
的 server 服务,可供 client 端提供主动连接的服务。
这个差异体现在下面
可以看到,在 Driver 构建整个结构的时候,在 NettyRpEnv 的 create 方法调用时会生成出一个 server,这个方法是RpcEnv
中的startServer
方法,而相同的 Executor 端的操作就不会有 server 构建出来。
这里需要说明一点,因为 spark 采用的是基于 Netty 的 RPC 通信,也就是裸在 TCP 协议上的直接网络连接,所以,server 端只能被 client 端主动连接,而不能 server 端主动连接 client,这个是常识就不多说了。但是一旦获取了稳定连接后,server 端持有了 client 端的 socket 信息,是可以主动向 client 端发送消息的;换句话说一旦连接上,client 端和 server 端相互持有对方的 socket,是双工随时可以向对方发送消息的,这一点不要陷入 http 协议的误区中。因为后续从 Driver 端下发任务给 Executor 其实就是 server 给 client 发消息的过程。
Driver 的 Server 的构建与 Endpoint 设置
看一下 server 构建的一些细节点,并关注在这个过程中,endpoint 是如何通过 Dispatcher 设置好的。
上面说到,server 端和 client 端不同的地方就是startServer
的过程,这个方法在RpcEnv
中。Netty
的 server 构建和 bootstrap 的原理这里就不做介绍了,后续有时间我会再去做一个Netty
的介绍,因为Netty
涉及到太多的Linux
底层和内存的原理,讲清楚真的不太容易。
代码如下
构建 server 的核心是
transportContext.createServer
的调用并且这个里面可以看到通过
dispatcher
设置了一个叫做RpcEndpointVerifier.NAME
的Endpoint
用前面的图可以回忆一下,这是一个非常简单的Endpoint
被塞入到Dispatcher
的过程,而其他的Endpoint
也是通过这个方法设置的,只不过场景和地方不一样,在后续的讲解中会慢慢一个个的涉及。
上述这个叫做RpcEndpointVerifier.NAME
的Endpoint
是做什么用处的呢?后续我会对一个完整的网络调用流程讲解的时候涉及到这个Endpoint
经过上面startServer
的方法的调用,Driver
端的 Server 就完全被启动起来了
Server 和 Client 的交互方式
在 Spark 中,一旦创建 Driver 所代表的 Server 端和 Executor 所代表的 Client 端是如何通信的呢?
基本的套路就是
Driver 端首先启动 Server
Executor 的 Client 端需要知道 Server 端的 ip 和 port,联通 Server
此时 Client 端持有了 Server 的 socket,同样,Server 也持有了 Client 的 socket,所以此时两方是可以随时向对方发送消息的。这块对于学习普通 http 网络的同学来说有点蒙,因为在 http 协议中,我们都是 Client 端发送 Request 请求到 Server 端,然后阻塞的等待 Server 返回,而没有 Server 端向 Client 端主动发送请求的 case,当然,websocket 除外。但,如果从 http 下降一层,我们就裸在 TCP 的上面,首次链接肯定是 Client 发送请求到 Server,经过在 TCP 层的三次握手,Client 端和 Server 端都会持有对方的全网唯一的 Socket,从此之后,双方谁都可以主动给对方发消息,一般来说,网络中肯定是多个 Client 对一个 Server(刨除 HA 的策略),Server 面对多个 Client 同时发送消息会有一些诸如 Netty 中的 NIO 的处理策略,而如果剔除掉这些策略,其实 Server 和 Client 完全对等,相互随时都可以给对方发消息,而收到消息的一方根据商议好的消息体中的类型可以回复或者不回复。了解到这里非常重要,因为后续从 Driver 端给 Executor 下发 task 的时候,其实就是 Server 发送消息给 Client 的过程,和我们平时一直理解的 Client 请求 Server 正好相反
图解一下交互过程
图很简单,想表达的意思就是从 Client 到 Server 端第一次的联通后,后续就是 Server 与 Client 端可以任意发送消息给对方。而关于途中的 1 和 N 的含义具体细化解释如下。
1 的含义
这个从 Client 到 Server 的 1 的含义,仅仅代表第一次的网络请求一定是从 Client 端发起联通到 Server 端的,是一个广义概念。从 TCP 层面来讲,肯定是要进行三次握手的过程。并且,在 spark 中,Executor 端作为 Client 首次与 Drvier 作为的 Server 联通的过程也是一个在 TCP 上层过程的“三次握手”的过程,这个过程在 spark 的源代码中写的非常优雅,下一章我会细聊这部分内容。
综上,初次链接是 Client 端发给 Server 的,也就是 Executor 发给 Driver 的,他们内部还会有一系列业务操作过程确认整个连接在业务层面已经 OK。
N 的含义
这里的 N 是一个双向箭头,即一旦双方建立起连接后,信息可以从任何一方发给对方。在上文也说过,这个过程不要和 http 协议中只有 client 发给 server 的过程弄混。因为 Spark 的网络是在 TCP 上层的自定义协议的封装,没有 http 的阻塞式的限制。
总结
本文主要介绍了在 Spark 中的业务层面的 Driver 和 Client 所代表的技术架构中的 Server 和 Client 之间的消息联通基本原理和过程,有点偏抽象,没关系,下篇我来给大家上一篇干货,从代码细节分析 Executor 是如何去连接 Driver 的,全部用图来画出,方便理解和记忆。
版权声明: 本文为 InfoQ 作者【dclar】的原创文章。
原文链接:【http://xie.infoq.cn/article/fb7ae4cc37b18bc1c213670a9】。文章转载请联系作者。
评论