在前两篇中,我们通过官网的example体验了livy的功能,留下了一个疑问,究竟livy是如何做到的呢?这一篇从源码里面找一下答案。

在直接分析源码前,先把结论通过时序图画出来,有个直观的映像:

image.png

  1. 客户端创建session,LivyServer收到请求后启动一个RpcServer。RpcServer会顺序选择一个从10000~10010之间的可用端口启动监听,假设此时是10000
  2. LivyServer随后通过SparkSubimit提交Application。Application在远端,最终会启动RSCDrvier
  3. RSCDrvier首先也从10000~10010之间顺序选择一个可用的端口,启动RpcServer。图中打出了关键的日志,第二篇中曾经提到过这个日志。
  4. RSCDrvier完成bind后,反向连接到LivyServer端的RpcServer。图中打出了关键的日志,第二篇中同样提到过。
  5. RSCDrvier主要向LivyServer所在的RpcServer上报自己bind的端口和ip。这一步其实就是最关键的步骤。
  6. RpcServer收到请求后将RSCDrvier的端口和ip封装成ContextInfo返回给LivyServer。同时关闭RpcServer
  7. LivyServer通过RSCDrvier的端口和ip连接到RSCDriver,从而完成tcp连接。至此session建立完成

以上就是简化的核心工作原理,可以通过netstat证实一下网络连接关系。vm3198是livyServer,vm3196是driver所在机器

下图是driver上的相关连接:

image.png

可以看到driver启用了10000端口的监听

下图是livyServer上的相关连接:

image.png

可以看到livyServer有一条连接到driver的链路,端口是可以对应上的

读者可能注意到,这里Driver的监听端口并不是10001,而是10000。这是因为虽然livyServer会率先占用10000,但由于Driver与livyServer不在一台机器上,所以对于Driver来说,10000当时并没有被占用,所以就使用10000端口了

注意到,我们在livyServer上并没有找到10000端口的监听。这是因为,一旦driver将自己的地址回发过来(通过回发RemoteDriverAddress消息),livyServer的Rpc监听就关闭了。

读者可能会考虑,RSCDrvier是如何知道livyServer的Rpc监听端点的呢?答案在启动Spark任务时上送的配置文件,我们摘取其中关键的配置项:

spark.__livy__.livy.rsc.launcher.address=vm3198
spark.__livy__.livy.rsc.launcher.port=10000

launcher.address/port;就是livyServer启动的Rpc监听端点。从RSCDriver的源码可以看到,程序从配置文件中读取了信息:

...
// Address for the RSC driver to connect back with it's connection info.
LAUNCHER_ADDRESS("launcher.address", null)
LAUNCHER_PORT("launcher.port", -1)
...

String launcherAddress = livyConf.get(LAUNCHER_ADDRESS);
int launcherPort = livyConf.getInt(LAUNCHER_PORT);
...
LOG.info("Connecting to: {}:{}", launcherAddress, launcherPort);

总结

本篇我们探究了livy的核心工作机制,了解了建立session时,在livyServer和Driver之间是如何建立连接关系的。更多关于rpc通信的细节有机会还会再基于源码详细展开分析


P_Chou水冗
5.1k 声望256 粉丝

大数据spark/flink/hadoop/elasticsearch/kafka架构与开发