大画 Spark :: 网络 (2)- 上篇 - 通过网络收取消息的过程

回顾
上一篇,https://xie.infoq.cn/article/3bac4574de003e458556a81d2,对 spark 网络进行了初探,了解了 client 端与 server 端大概的构成,以及一个非常简单的交互模型。

重点
消息分为
RpcRequest
和OneWayMessage
两类RpcRequest
:client 端发送,需要 server 端进行 response 返回,返回后在 client 端处理完成 response,一去一回,类似于传输层的 TCP 协议OneWayMessage
:相反,一去不返的方式,类似于传输层的 UDPserver 端对于 request 的 response 返回,使用的是 callback 调用的方式
client 端对于收到 response 后,识别到底是对应的哪一个 request,采用的是缓存一个 map 数据结构的方式,通过 requestId 找到相应的 callback 的方式
本篇主旨
对于上图,我们高度抽象成下图。本篇解读当服务端收到消息后
如何把消息传递到 spark 处理业务的核心组件
在这个过程中涉及到的 Dispatcher、endpoint、endpointRef 到底是什么
这些组件是如何相互配合完成工作的
处理
RpcRequest
和处理OneWayMessage
的区别
注意
读者可以用图当作架构说明文档,对照代码一起来看,这样效果最好

网络服务的基础:NettyRpcEnv
对于接收到的消息,打开 spark 核心业务处理的桥接通道就是NettyRpcEnv
的NettyRpcHandler
的receive
方法

回忆 NettyRpcEnv
上一篇https://xie.infoq.cn/article/3bac4574de003e458556a81d2 提到了下面这幅图
我把原图做了一个镜像,网络世界放到了左侧,为了和上面的图可以合成到一起。
可以看到当我们需要通过 client 端访问外面的服务的时候,最终需要使用
NettyRpcEnv
调用TransportClient
,发起的是RpcEndpointRef
的 ask 或者 send(这个概念后面细说)并且,上一篇我也提到了
NettyRpcEnv
也是构成所有网络服务的基础,下文会细讲

NettyRpcEnv 是消息处理的双工桥梁
NettyRpcEnv
是基础,但是充当桥梁的是它构建的其他组件,如RpcEndpointRef
,如NettyRpcHandler

再细化一下,选取 2 个有代表性的方法,send、ask
和receive

和外部网络图合并来理解一下

整体的层次
我们先来总结一个层次图,罗列起来比较能看得清层次感

Spark 的逻辑处理世界的宏观结构
上图中引入和“Spark 的逻辑处理世界”这个概念,到这里就基本上解藕了网络层,来到业务处理的部分
High level 看逻辑处理过程
是的,你没看错,其实大的逻辑处理过程就是这么素颜。我很喜欢把复杂问题先简单化,然后再逐步深入细节,从而避免从入门到放弃,打造一个从入门→兴趣→钻研→成功的过程。

上图中,Dispatcher
到RpcEndpoint
是虚线,说明这个调用不是一个线性直接调用的过程,中间涉及到了线程池等复杂调用。
解释 Dispatcher
稍稍有些经验的技术同学看到这个名词一定会想到DispatcherServlet
,嗯,是的,我就希望这么记忆这个组件,它的大概功能也基本相当,就是一个前置控制器,拦截请求,匹配后续的操作。
解释 RpcEndpoint (trait)
RpcEndpoint
是trait
,也就是 java 中我们经常说的接口。它有很多的实现类,比如

RpcEndpoint
的实现类就是最终实现收到消息,处理这个消息的逻辑业务的部分
Dispatcher 与 RpcEndpoint 的关系
根据上面的描述,可以知道Dispatcher
就是根据 message 的类型,选择不同的RpcEndpoint
的实现类来执行业务处理

Dispatcher 的构建
上一章的图经过加工就是下图,Dispatcher
也是通过NettyRpcEnv
构建出来的
构建关系

层次关系
通过下图看层次关系

深入一下 Spark 的逻辑处理世界
NettyRpcHandler→Dispatcher
通过 2 个receive
方法联通起Dispatcher
。前文说过,因为有单向OneWayMessage
和带反馈的RpcRequest
,所以会对应 2 个 receive 方法和 2 个 post...Message 方法,如下图所示

NettyRpcHandler→Dispatcher→RpcEndpoint
因为Dispatcher
最终会选择一个RpcEndpoint
的实例来处理请求,所以会是这样的一种关系,注意,图中的虚线代表不是直接调用,而是“最终会调用到”的意思
OneWayMessage
,会在Dispatcher
的postOneWayMessage
方法处理,从而最终调用到RpcEndpoint
实例的receive
方法RpcRequest
,会在Dispatcher
的postRemoteMessage
方法处理,从而最终调用到RpcEndpoint
实例的receiveAndReply
方法
注意图中的 A、B、C
A:代表一个既可以接收
OneWayMessage
又可以接收RpcRequest
消息的RpcEndpoint
实例B:代表一个只可以接收
RpcRequest
消息的RpcEndpoint
实例C:代表一个只可以接收
OneWayMessage
消息的RpcEndpoint
实例
所以,通过下图也可以看出
如果一个
RpcEndpoint
中包含receive
方法, 那么它只可以接收OneWayMessage
消息,如果一个
RpcEndpoint
中包含receiveAndReply
方法, 那么它只可以接收RpcRequest
消息如果一个
RpcEndpoint
中包含上述 2 种方法, 那么它既可以接收RpcRequest
消息也可以接收OneWayMessage
消息

解读 RpcEndpoint
说到这里,根据上面的各类图,为了理解RpcEndpoint
我们可以做一个技术的横向类比。
先看一个简单的 SpringMVC 的例子
如果访问/main/index 的 RESTful 的接口,则是 AClass 的 method1 来进行处理,这个大家都很清楚。后续会用 SpringMVC 的例子做对比记忆,非常简单。
我们都知道被@Controller
注解注释的 class,在 Spring 启动的时候会进行扫描,对所有注释的方法进行管理,对每一个请求进行@Controller
到@RequestMapping
的 path 的匹配,如下图
通过
/main
找到 AClass通过
/index
找到 method1,最终由 method1 来处理这个请求
以上的过程就是 SpringMVC 中的基本原理

什么是 RpcEndpoint
先说结论,RpcEndpoint
的实例就可以理解为 SpringMVC 中的@Controller
注释的类。只不过在 spark 中不是注解,而是需要所有的 class 来实现RpcEndpoint
的 trait
定义 RpcEndpoint
RpcEndpoint
翻译过来是端点,顾名思义,一个请求可以触及到的最终的目的地。这个目的地也是处理请求的地方
类比 RpcEndpoint 和 SpringMVC 中处理请求的过程
左侧是 SpringMVC 的例子,右侧是 spark 中的RpcEndpoint
,很像对吧。我们对比来说
spark 中的消息请求分为 1-4 的 4 个部分
我用一个 spark 中的探活的
RpcEndpoint
:RpcEndpointVerifier
举例
ip 和 port,用于找到服务器,这个和左侧一样
消息类型,用于判断到底是用
receiveAndReply
方法来执行还是用receive
方法来执行请求的
RpcEndpoint
的名称,这个很重要,通过这个可以找到到底用那个RpcEndpoint
来执行,这个就可以理解成为左侧的@Controller
中定义的 path,如/main
消息体类型,粉色,即,这个消息具体的类型,用于在
RpcEndpoint
中进行 case calss 的模式匹配见右图

引出 RpcEndpointRef 的概念
通过上面的图,我们可以知道,如果想访问上面例子中的RpcEndpointVerifier
的RpcEndpoint
的话,起码需要 3 个必要条件,即上图中的
1: ip 和 port
3:
RpcEndpoint
的唯一识别名称:name4:需要模式匹配的 case class,也就是消息体类型
以上 No.1 和 No.3 的 2 个条件即可唯一识别出可以精准访问到RpcEndpoint
,而具体访问到哪个 case class 的模式匹配处理,则需要上面的 No.3 在消息中动态的来指定
spark 将上面的三个东东抽象成了一个结构,叫做RpcEndpointRef
,是一个抽象类,其实现类叫做NettyRpcEndpointRef
。讲的有点干,我们画个图
RpcEndpointRef 是 RpcEndpoint 的引用

通过 RpcEndpointRef 访问 RpcEndpoint
RpcEndpoint
所对应的RpcEndpointRef
会被另一台服务器所持有,通过 send 方法的调用来访问到RpcEndpoint
本尊。至于下图中,在 client 端是如何获取到RpcEndpoint
的RpcEndpointRef
,我们后面会用一个详细的案例来讲述

总结:RpcEndpointRef → RpcEndpoint

阶段性总结消息到 RpcEndpoint 的流程
以
RpcRequest
的消息为例,整个 1-6 的过程如下图所示,下一篇我们会把 5 再做细化的讲解

总结
本篇,我们从串通了从NettyRpcHandler
到最后的RpcEndpoint
的大概流程,在这个过程中,我们弄清了以下几个概念
Dispatcher
:前置控制器,用于选择消息到底发送到哪里RpcEndpoint
:处理消息的最终的端点的一个 trait,里面含有处理消息的业务逻辑RpcRequest
的消息:调用receiveAndReply
方法OneWayMessage
的消息:调用receive
方法RpcEndpointRef
:描述RpcEndpoint
的基础信息,包括 ip、port、和 name,利用这三个基本属性可以找到在某一台服务器的RpcEndpoint
,利用RpcEndpointRef
的 ask 或者 send 方法即可访问当相应的RpcEndpoint
我们用了 SpringMVC 的方式类比了消息处理的过程,方便小伙伴的理解
下一篇,我们详细介绍Dispatcher
→RpcEndpoint
的具体流程,这是一个很有意思的过程。
版权声明: 本文为 InfoQ 作者【dclar】的原创文章。
原文链接:【http://xie.infoq.cn/article/9b7e1ebf236f7a4abcc81311c】。文章转载请联系作者。
评论