Spark Submit的ClassPath问题

需求场景: 我们的产品需要与客户的权限系统对接,即在登录时使用客户的认证系统进行认证。集成认证的方式是调用客户提供的jar包,调用userServiceauthenticate方法。同时,还需要在classpath中提供密钥的key文件。

从需求看,这个集成并不复杂,且客户也提供了较翔实的接口文档与示例案例,开发工作量非常小。唯一的阻碍是客户有安全要求,内部的Jar包及其他文件都不能拷贝出来,而我们的开发环境是不能连接客户内网的。客户提供的Jar包并没有通过Maven来管理,我们只能采用直接导入的方式。在我们的Scala项目中,可以直接将要依赖的jar包放在module的lib文件夹下,在使用sbt执行编译和打包任务时,会自动将lib下的jar包放入classpath中。

那么,需要解决的第一个问题是:由于客户的jar包不能拷贝到我的开发环境中,该如何处理该依赖?

既然在开发环境下拿不到这个jar包,那就做一个mock包吧。幸而需要编写的代码仅仅牵涉到ServiceConfigServiceManagerUserService三个类以及这些类的少数方法。其中ServiceConfig提供了认证需要的属性值,并通过set方法进行设置。因为最终需要调用的其实是UserServiceauthenticate方法,只需要为其提供一个简单的实现,并定义好其他相关的类型与方法,保证编译能够通过即可。

第一个问题轻松解决。

由于我们使用了sbt assembly,并编写了对应的脚本来支持整个产品的打包工作,最终打包的结果是一个完整的mort.jar包。换言之,我们要依赖的外部Jar包也将被打包到最终的jar文件中。故而,第二个问题接踵而来:既然程序代码与外部jar包都被打包到最终的部署包中,当我们将该包拷贝到客户的部署环境中后,该如何将之前的mock包替换为真正的实现呢?

实际上,sbt assembly并不会将所有依赖的外部包都装配到最终的部署包中,只要在sbt的依赖中添加provided,就能保证第三方依赖包不被包含进部署包中。因此,我们可以改写sbt脚本,当执行assembly时,排除这个mock包,这是首要解决的方案。方法是在build.sbt中添加如下脚本:

excludedJars in assembly := {
  val cp = (fullClasspath in assembly).value
  cp filter {_.data.getName == "customer_provided_mock.jar" }
}

部署包确实不再包含这个外部依赖包了,但是在部署时,我们还得将真实的jar包放入到部署环境的classpath中。然而事与愿违,当我们将真正的jar包放在本地的classpath中时,运行时却找不到这个jar包。问题出现在哪里?

原因在于我们的程序并非一个普通的java程序,而是一个spark application,部署环境则为集群环境,运行该程序是通过spark submit的方式,将部署包提交到spark的cluster manager。这就需要分析spark submit的工作原理,如下图所示:

在集群部署模式下,Driver端通过spark-submit将spark application提交到集群,然后分发到Job到Worker节点。我们系统的主程序入口为com.bigeyedata.mort.Main,程序的运行是通过spark-submit去调用部署包的Main,即在spark driver下运行,而非在本地通过java启动虚拟机执行mort.jar。

这就是在本地设置classpath不生效的根本原因。

我注意到spark-submit提供了--jar参数,除了spark application这个jar包之外的其他jar包,都可以通过这个参数指定包,从而将其自动传送给集群。注意,若--jar指定了多个jar包,则通过分隔符,分隔,这与--driver-class-path的分隔符不同,后者使用:。因此,我修改了启动程序的脚本,将其设置为:

exec $SPARK_HOME/bin/spark-submit \
--class com.bigeyedata.mort.Main \
--driver-class-path $MORT_HOME/libs/*.jar \
--master yarn-client \
--deploy-mode cluster \
--jars /appcom/mort/thirdparty_jars/customer_provided.jar \
--queue queue_0100_01 \
$MORT_HOME/mort.jar > $MORT_HOME/mort.log 2>&1

还有一个问题需要解决:如何放置用户认证需要的密钥key文件?

该文件仍然不能作为内嵌的资源文件打包到部署包中。因为这个文件的内容需要区分测试环境和生产环境。在部署到生产环境中时,需要替换为另一个key文件。客户的文档说明,需要将该文件(不是jar文件)放到运行的classpath中。

解决办法如前,仍然不能直接将key文件放入到本地的classpath中,而是利用spark-submit的--files参数。故而需要在前面的脚本中,为spark-submit添加如下内容:

--files /appcom/mort/thirdparty_jars/clientKey.pk \

三个问题给我制造了一定的麻烦,尤其是第二个问题的解决,又让我温习了spark submit的工作原理,了解相关参数的作用。虽然花费了一些时间,但问题的解决还是颇有价值的。

原文发布于微信公众号 - 逸言(YiYan_OneWord)

原文发表时间:2017-06-10

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java进阶架构师

理解数据库连接池底层原理之手写实现

数据库连接池的基本思想是:为数据库连接建立一个“缓冲池”,预先在池中放入一定数量的数据库连接管道,需要时,从池子中取出管道进行使用,操作完毕后,在将管道放入池子...

1012
来自专栏编程语言

Python:VScode 设置Python不同版本运行环境

1.首先本地安装有不同的Python的版本,比如2.7和3.5.mac下一般默认自带2.7版本。  终端输入侧  python -V 则显示默认版本号。而p...

1102
来自专栏我的博客

dedecms安装

安装之前需要准备 空间可以正常使用PHP+MYSQL 空间数据库用户名和密码 第一步:下载后解压uploads文件夹到指定的文件夹内(我这使用的www/dede...

3357
来自专栏吴伟祥

Nginx认识与实践(一) 转

笔者按照文章:《Nginx服务器开箱体验》 中的实验,给出了一份简要的清单配置举例:

903
来自专栏xcywt

《Linux命令行与shell脚本编程大全》第十五章 呈现数据

15.1 理解输入和输出 现在知道两种显示脚本输出的方法 1)在显示器屏幕上显示 2)将输出文件重定向到文件中 15.1.1 标准文件描述符 Linux系统将每...

2106
来自专栏存储

spring boot redis分布式锁

随着现在分布式架构越来越盛行,在很多场景下需要使用到分布式锁。分布式锁的实现有很多种,比如基于数据库、zookeeper等,本文主要介绍使用Redis做分布式锁...

5325
来自专栏一枝花算不算浪漫

[Linux基础]Linux基础知识入门及常见命令.

3807
来自专栏木头编程 - moTzxx

CentOS下(rsync+crontab)触发式实现服务器间的文件备份同步

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011415782/article/de...

1622
来自专栏令仔很忙

新手学Linux(八)----MySql安装(Centos7)

最近项目新申请了云服务器,老大让我搭了下环境,在安装MySql的时候遇到的坑稍微多点,所以在这里做个记录,我的安装方式不是通过安装包来安装的,而是通过yum来...

1091
来自专栏JMCui

Hybris 项目工程配置

1、控制台页面进入platform目录 cd F:\hybris640\hybris\bin\platform 并运行 setantenv.bat 生成对应的a...

5019

扫码关注云+社区