首页 理论教育 应用程序部署优化:最佳实践

应用程序部署优化:最佳实践

时间:2023-06-29 理论教育 版权反馈
【摘要】:2.以Cluster的部署模式提交应用程序结合“3.2.2应用部署的源代码解析”中的内容来看,在Cluster的部署模式提交时,Spark会将应用程序封装到指定的类中,由该类负责向集群申请提交应用程序的执行。

应用程序部署优化:最佳实践

和其他常见的分布式集群类似,Spark Standalone集群的部署也是采用典型的Master/ Slave架构。其中,Master结点负责整个集群的资源管理与调度,Worker结点(也可以称Slave结点)在Master结点的调度下启动Executor,负责执行具体工作(包括应用程序及应用程序提交的任务)。

从前面的分析中抽取出Spark Standalone模式部署的TaskScheduler与SchedulerBackend具体子类的实例构建信息,如表3-8所示。

表3-8 Spark Standalone模式部署具体子类的构建

978-7-111-55442-4-Chapter03-48.jpg

下面以提交请求的行为为例,结合应用程序提交时所使用的不同部署模式,给出详细的框架及其描述,对应在框架中的其他请求与此类似,可以自行解析。

1.以Client的部署模式提交应用程序

在Client的部署模式提交时,直接在提交点运行应用程序,即对应的驱动程序是在当前结点启动的。启动一个应用程序后,就涉及各个相关的方面,包含应用运行的环境、应用元数据的清理、状态监听、DAG调度、任务调度等。

对应的部署与执行框架如图3-3所示。

978-7-111-55442-4-Chapter03-49.jpg

图3-3 Client部署模式下的部署与执行框架

如图3-3所示,在驱动程序(Driver Program)内部会构建一个SparkContext实例,在前面章节中已经分析过,在SparkContext实例的主要流程中,会构建出用于任务调度的TaskScheduler实例、用于DAG调度的DAGScheduler实例,以及作为TaskSchedulerImpl底层的一个可插拔的调度系统终端的SparkDeploySchedulerBackend实例。由于调度系统后续会用单独一章的篇幅进行解析,因此这里仅仅给出简单的部署与交互过程,具体过程如下。

1)在SparkContext构建出SparkDeploySchedulerBackend实例后,调用该实例的start方法,关键代码如下。

978-7-111-55442-4-Chapter03-50.jpg

978-7-111-55442-4-Chapter03-51.jpg

其中,AppClient实例在调用方法start时,会构建一个RPC通信终端,即ClientEndpoint实例,实例化后再自动调用onStart(),这时就会将封装的ApplicationDescription实例进一步封装到消息RegisterApplication的实例中,然后由该RPC通信终端将该信息发送到Master的RPC通信终端。

2)Master的RPC通信终端在收到RegisterApplications消息后,通过资源调度方法,最终会调用launchExecutor方法,在该方法中再向调度所分配到的Worker结点的RPC通信终端发送LaunchExecutor消息。

3)Worker的RPC通信终端在收到LaunchExecutor消息后,会实例化ExecutorRunner对象,然后启动一个线程,在线程中解析RegisterApplications消息封装的ApplicationDescription实例所携带的Command实例,也就是前面封装的CoarseGrainedExecutorBackend类,最后启动CoarseGrainedExecutorBackend类的进程。进程的入口就是CoarseGrainedExecutorBackend伴生对象的main函数。

4)在入口处,即CoarseGrainedExecutorBackend伴生对象的main函数中,会解析参数,然后调用run函数,在该run函数中会构建CoarseGrainedExecutorBackend实例,也就是构建一个RPC通信终端。Run方法中的关键代码如下。

978-7-111-55442-4-Chapter03-52.jpg

978-7-111-55442-4-Chapter03-53.jpg

其中,driverUrl是封装CoarseGrainedExecutorBackend到Command时设置的,可以回到前面SparkDeploySchedulerBackend实例的start方法,在构建Command之前,设置了一些参数,对应的代码如下。

978-7-111-55442-4-Chapter03-54.jpg

其中,第4、6行就是封装的与CoarseGrainedExecutorBackend进行通信的终端及其对应参数的选项名称,也就是前面的CoarseGrainedSchedulerBackend实例的driverEndpoint:Driv-erEndpoint成员,对应在Spark Standalone部署模式下,就是具体子类SparkDeployScheduler-Backend。

5)对应CoarseGrainedExecutorBackend的RPC通信终端,在实例化时自动调用on-Start方法。在该方法中向driverUrl发送RegisterExecutor消息。这里的driverUrl就是上一步分析得到的驱动程序端的SparkDeploySchedulerBackend的RPC通信端口driver-Endpoint。

6)SparkDeploySchedulerBackend实例收到RegisterExecutor消息时,表示当前有可用资源注册上来,此时即可开始作业的调度。具体的调度过程可以参考本书的第4章。

说明:分布式消息间的调度是建立在消息事件的基础上的,可以通过列举所有的RPC通信终端(系统名+终端名)和各个终端之间交互的信息这两个方面,来理清整个分布式集群中各个组件之间和内部的交互逻辑,进而把握整个调度机制。

2.以Cluster的部署模式提交应用程序

结合“3.2.2应用部署的源代码解析”中的内容来看,在Cluster的部署模式提交时,Spark会将应用程序封装到指定的类中,由该类负责向集群申请提交应用程序的执行。即在Cluster部署模式提交时,通过向Master申请执行应用程序,然后由Master负责调度分配一个Worker结点,并向该结点发送启动应用程序的消息,应用程序启动后的执行流程,与在该Worker结点上直接以Client部署模式提交应用程序的执行流程是一样的,只是在此之前需要先调度得到该Worker结点,并在该结点启动应用程序。由于在申请到资源并启动提交的应用程序之后,这一执行框架和Client部署模式下是一样的,因而在此仅关注将应用程序封装到指定类,并与集群Master进行交互的执行框架。

再次查看对应封装的类,如表3-9所示。

表3-9 Cluster的部署模式提交时具体封装的子类

978-7-111-55442-4-Chapter03-55.jpg

下面根据不同的提交方式分析提交应用程序的请求,对应其他的请求行为,比如Kill应用程序或获取应用程序的状态等,都可以借鉴提交应用的执行框架的解析来加深理解。

说明:注意Client与Cluster两种部署模式下,首次提交申请的对象是不同的,Clinet时是直接提交应用程序的申请,Cluster时会先提交应用程序本身的申请,结合前面的部署框架(见图3-2),通常应用程序也称为驱动程序(Driver Program),因此该部署模式下首次提交的申请对应的是Driver的申请,在代码中这两种模式分别对应的申请的描述信息为ApplicationDescription与DriverDescription。

(1)提交方式为REST方式(Spark 1.3+)

当提交方式为REST方式(Spark 1.3+)时,Spark会将应用程序的主类等信息封装到RestSubmissionClient类中,由该类负责向RestSubmissionServer发送提交应用程序的请求,而RestSubmissionServer接收到应用程序提交的请求之后,会向Master发送RequestSubmitDriver消息,然后由Master根据资源调度策略,启动集群中相应的Driver,执行提交的应用程序。详细的执行框架如图3-4所示。

978-7-111-55442-4-Chapter03-56.jpg

图3-4 Cluster部署模式下的部署与执行框架

为了体现各个组件间的部署关系,这里以框架图的形式进行描述,相应的,可以从时序图的角度去理解各个类或组件之间的交互关系。其中组件Master和Worker的标注在方框的左上角,其他方框表示一个具体的实例。

其中,RestSubmissionClient是提交应用程序的客户端处,是对提交的应用程序进行封装的类。之后各个组件间的交互流程分析如下。

1)第1步constructSubmitRequest,就是在RestSubmissionClient实例中,根据提交的应用程序信息构建出提交请求。(www.xing528.com)

2)然后继续第2步createSubmission,在该步骤中向RestSubmissionServer发送post请求,即图3-4中对应的第3步(注意,实际上是在第2步中调用)。

3)RestSubmissionServer接收到post请求后,由对应的Servlet进行处理,这里对应为StandaloneSubmitRequestServlet。即开始第4步,调用doPost,发送Post请求。

4)doPost中继续第5步handleSubmit,开始处理提交请求。在处理过程中,向Master的RPC终端发送消息RequestSubmitDriver,对应图3-4中的第6步。

5)Master接收到该消息后,执行第7步createDriver,创建Driver,创建时需要由Master的调度机制(对应第8步schedule)获取分配的资源后,向Worker(这些Worker启动时会注册到Master上)的RPC终端发送LaunchDriver消息。

6)Worker在RPC终端接收到消息后开始处理并实例化一个DriverRunner,并运行之前封装的应用程序。

978-7-111-55442-4-Chapter03-57.jpg

注意:从上面的部署框架及其术语解析部分可以知道,由于提交的应用程序在main部分包含了Spark-Context实例,因此也称之为Driver Program,即驱动程序。因此在框架中,对应在Master和Worker处都使用Driver,而不是Application(应用程序)。

其中主要的源代码及其分析如下。

1)RestSubmissionClient的run方法的代码如下。

978-7-111-55442-4-Chapter03-58.jpg

2)收到提交的Post请求之后,StandaloneSubmitRequestServlet向Master的RPC终端发送请求,代码如下。

978-7-111-55442-4-Chapter03-59.jpg

3)构建DriverDescription的buildDriverDescription方法的代码如下。

978-7-111-55442-4-Chapter03-60.jpg

978-7-111-55442-4-Chapter03-61.jpg

4)Master接收并处理消息的代码如下。

978-7-111-55442-4-Chapter03-62.jpg

5)Master的schedule():调度机制的调度代码如下。

978-7-111-55442-4-Chapter03-63.jpg

978-7-111-55442-4-Chapter03-64.jpg

6)Worker上的Driver启动的代码如下。

978-7-111-55442-4-Chapter03-65.jpg

978-7-111-55442-4-Chapter03-66.jpg

(2)提交方式为传统方式

这里的传统方式指的是在REST方式之前,Spark所提供的提交方式。此时会将应用程序的主类等信息封装到org.apache.spark.deploy.Client类中,该类和REST方式下的封装类RestSubmissionClient一样,也会将提交的应用程序封装到DriverDescription,然后向Master发送RequestSubmitDriver消息,之后的Master消息处理与调度等步骤与REST方式一样。详细的执行框架如图3-5所示。

978-7-111-55442-4-Chapter03-67.jpg

图3-5 传统提交方式下的详细执行框架

为了体现各个组件间的部署关系,这里以框架图的形式进行描述,相应的,可以从时序图的角度去理解各个类或组件之间的交互关系。其中组件Master、Worker和Client的标注在方框的左上角,其他方框表示一个具体的实例。其中需要注意的是虚线方框处是该提交方式与REST方式之间的差异所在。

和REST方式相比,传统的方式只在向Master发送提交应用请求的地方不同,其他步骤都一样。其中,Client是提交应用程序的客户端处,对提交的应用程序进行封装的类。之后各个组件间的交互流程分析如下。

1)第1步在Client的入口函数main中,在该函数中会构建一个与Master进行通信的RpcEndPoint,即ClientEndpoint实例。在ClientEndpoint实例构建后,会调用该实例的onStart方法,然后向Master的通信端口发送请求消息RequestSubmitDriver,然后等待反馈并退出。

2)Master接收到该消息后,执行第2步createDriver,创建Driver,创建时需要由Master的调度机制(对应第3步schedule),获取分配的资源后,向Worker(这些Worker启动时会注册到Master上)的RPC终端发送LaunchDriver消息。

3)Worker在RPC终端接收到消息后开始处理,实例化一个DriverRunner,并运行之前封装的应用程序。

其中2)、3)和REST方式的处理流程一样。

在传统方式提交的执行框架中,主要的源代码及其分析如下。

1)Client的入口函数main的代码如下。

978-7-111-55442-4-Chapter03-68.jpg

2)ClientEndpoint的onStart方法的代码如下。

978-7-111-55442-4-Chapter03-69.jpg

978-7-111-55442-4-Chapter03-70.jpg

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈