Spark Submit的ClassPath问题
需求场景: 我们的产品需要与客户的权限系统对接,即在登录时使用客户的认证系统进行认证。集成认证的方式是调用客户提供的jar包,调用
userService
的authenticate
方法。同时,还需要在classpath中提供密钥的key文件。
从需求看,这个集成并不复杂,且客户也提供了较翔实的接口文档与示例案例,开发工作量非常小。唯一的阻碍是客户有安全要求,内部的Jar包及其他文件都不能拷贝出来,而我们的开发环境是不能连接客户内网的。客户提供的Jar包并没有通过Maven来管理,我们只能采用直接导入的方式。在我们的Scala项目中,可以直接将要依赖的jar包放在module的lib文件夹下,在使用sbt执行编译和打包任务时,会自动将lib下的jar包放入classpath中。
那么,需要解决的第一个问题是:由于客户的jar包不能拷贝到我的开发环境中,该如何处理该依赖?
既然在开发环境下拿不到这个jar包,那就做一个mock包吧。幸而需要编写的代码仅仅牵涉到ServiceConfig
、ServiceManager
与UserService
三个类以及这些类的少数方法。其中ServiceConfig
提供了认证需要的属性值,并通过set方法进行设置。因为最终需要调用的其实是UserService
的authenticate
方法,只需要为其提供一个简单的实现,并定义好其他相关的类型与方法,保证编译能够通过即可。
第一个问题轻松解决。
由于我们使用了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的工作原理,了解相关参数的作用。虽然花费了一些时间,但问题的解决还是颇有价值的。
- 《Android 创建线程源码与OOM分析》
- 微信 Android 视频编码爬过的那些坑
- 少年,这有套《街霸2》AI速成心法,想传授于你……
- 你知道android的MessageQueue.IdleHandler吗?
- 《Android基础:Fragment,看这篇就够了》
- Android 7.0中ContentProvider实现原理
- 《iOS APP 性能检测》
- iOS 11 安全区域适配总结
- Linux下巧用chattr、watch命令的实例
- 【特斯拉组件】iOS高性能PageController
- SUSE Linux系统在线安装软件命令zypper参数详解
- Linux下通过rdesktop连接Windows远程桌面
- 微信iOS收款到账语音提醒开发总结
- React Native按需加载 手Q狼人杀探索之路
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Vue.js 3 正式进入 RC 阶段
- FeignClient注解及参数问题---SpringCloud微服务
- Rust FFI 编程 - Rust导出共享库01
- 【每周一库】 see - An HTTP server for hosting static files
- 【Rust日报】2020-07-17 无船同志新博客:Shipping Const Generics in 2020
- SpringCloud--Config Server配置中心学习总结
- 【Rust日报】2020-07-18 提升ARM64 Linux平台支持到Tier-1
- SpringCloud+MyBatis(oracle)逆向工程自动生成代码
- JDK1.8的几个简单Lambda表达式
- 腾讯云直播开发日记 (一) 直播播放和观看
- Docker六脉神剑(二) 使用Docker构建lnmp开发环境
- redis主从配置+集群搭建
- 【Ceph RGW】找一个用Go写的s3cmd
- 技术分享 | 如何优雅地在 Windows 上从 MySQL 5.6 升级到 5.7
- SpringCloud分布式配置中心