如何为Spark Application指定不同的JDK版本详解

前言

随着企业内部业务系统越来越多,基于JVM的服务,通常情况线上环境可能会有多套JDK跑不同的服务。大家都知道基于高版本的Java规范编写的服务跑在低版本的JVM上会出现:java.lang.UnsupportedClassVersionError的异常。

Spark 2.2开始移除了对Java 7的支持,大多数情况下,我们的Spark Application是和Hadoop系统公用的JDK,如果Hadoop依赖的JDK版本是7,那我们基于JDK 8编写的Application跑在上面就会出问题。

该文主要介绍在不同的场景下,如何为Spark Application指定不同的JDK版本。

集群已部署了指定的JDK版本

假设集群中每个节点JDK的部署路径为:/usr/java/jdk1.8

Spark提供了spark.executorEnv.[EnvironmentVariableName]配置,可以用来给Executor进程添加环境变量,如果Spark Application使用的集群管理器是Standalone,只需要通过spark.executorEnv.JAVA_HOME制定Executor端的jdk路径即可,如下:

$SPARK_HOME/bin/spark-submit \
 --conf "spark.executorEnv.JAVA_HOME=/usr/java/jdk1.8" \
 ...

在YARN模式下,还需要为Application Master指定不同的JAVA_HOME环境变量,如下:

$SPARK_HOME/bin/spark-submit \
 --conf "spark.executorEnv.JAVA_HOME=/usr/java/jdk1.8" \
 --conf "spark.yarn.appMasterEnv.JAVA_HOME=/usr/java/jdk1.8" \
 ...

以cluster的方式部署在YARN上的时候,spark.yarn.appMasterEnv.JAVA_HOME相当于为Spark Application的Driver设置了特定的JDK版本;

以client的模式部署时,spark.yarn.appMasterEnv.JAVA_HOME仅仅是为Executor Launcher设置了特定的JDK版本。

Driver端的JDK版本和spark-submit所在的机器中的SPARK_HOME环境变量一致,直接在spark-env.sh中指定即可。

集群缺失特定的JDK版本,且对集群无管理权限

某些特殊的场景下,我们对集群没有管理权限,只能通过YARN提交Application,并且集群里没有部署我们需要的JDK版本,这种情形就需要将JDK的安装包也一并提交了。

这里要求我们的JDK安装包必须为gz格式的,和你代码打包后的jar包放在同一目录下,假设我们下载的JDK的安装包为:jdk-8u141-linux-x64.tar.gz。

关键配置如下:

$SPARK_HOME/bin/spark-submit \
 --conf "spark.yarn.dist.archives=jdk-8u141-linux-x64.tar.gz" \
 --conf "spark.executorEnv.JAVA_HOME=./jdk-8u141-linux-x64.tar.gz/jdk1.8.0_141" \
 --conf "spark.yarn.appMasterEnv.JAVA_HOME=./jdk-8u141-linux-x64.tar.gz/jdk1.8.0_141" \
 ...

我们可以通过指定spark.yarn.dist.archives配置,将JDK的安装包分发到所有Executor的工作目录下(包括Application Master的Executor),另外tar.gz的压缩包也会被自动解压,假设jdk-8u141-linux-x64.tar.gz解压后的目录为jdk1.8.0_141,那么我们特定的JDK的目录就是:./jdk-8u141-linux-x64.tar.gz/jdk1.8.0_141,不同的JDK版本以此类推即可。

注意:由于Spark Standalone没有提供分发JDK安装包并自动解压的功能,所以,这种方式只能用在YARN下。

验证

通过ps -ef grep查询相关进程信息,可以看到java的启动路径为我们特定JDK目录的java表示配置成功。

如下是我在YARN模式下,单独指定JDK版本的Executor的进程启动信息:

stan  590751 590745 0 20:45 ?  00:00:14 ./jdk-8u141-linux-x64.tar.gz/jdk1.8.0_141/bin/java -server -Xmx512m -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+G1SummarizeConcMark -XX:InitiatingHeapOccupancyPercent=35 -XX:PermSize=256M -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:./gc.log -verbose:gc -Djava.io.tmpdir=/home/stan/tmp/hadoop-stan/nm-local-dir/usercache/stan/appcache/application_1508397483453_0095/container_1508397483453_0095_01_000004/tmp -Dspark.driver.port=52986 -Dspark.yarn.app.container.log.dir=/home/stan//hadoop-2.6.4/logs/userlogs/application_1508397483453_0095/container_1508397483453_0095_01_000004 -XX:OnOutOfMemoryError=kill %p org.apache.spark.executor.CoarseGrainedExecutorBackend --driver-url spark://CoarseGrainedScheduler@10.0.0.110:52986 --executor-id 3 --hostname stan --cores 1 --app-id application_1508397483453_0095 --user-class-path file:/home/stan/tmp/hadoop-stan/nm-local-dir/usercache/stan/appcache/application_1508397483453_0095/container_1508397483453_0095_01_000004/__app__.jar

附:spark application运行时版本不兼容错误的解决方法

17/06/27 14:34:41 INFO deprecation: mapred.map.tasks is deprecated. Instead, use mapreduce.job.maps
17/06/27 14:34:41 INFO MemoryStore: Block broadcast_0 stored as values in memory (estimated size 788.8 KB, free 1246.5 MB)
17/06/27 14:34:41 INFO MemoryStore: Block broadcast_0_piece0 stored as bytes in memory (estimated size 54.0 KB, free 1246.4 MB)
17/06/27 14:34:41 INFO BlockManagerInfo: Added broadcast_0_piece0 in memory on 10.50.70.121:37335 (size: 54.0 KB, free: 1247.2 MB)
17/06/27 14:34:41 INFO SparkContext: Created broadcast 0 from rdd at TradeInfoOutlier.scala:30
Exception in thread "main" java.lang.NoSuchMethodError: scala.reflect.api.JavaUniverse.runtimeMirror(Ljava/lang/ClassLoader;)Lscala/reflect/api/JavaUniverse$JavaMirror;
at com.fangdd.data.profile.outlier.TradeInfoOutlier$.main(TradeInfoOutlier.scala:30)
at com.fangdd.data.profile.outlier.TradeInfoOutlier.main(TradeInfoOutlier.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.spark.deploy.SparkSubmit$.org$apache$spark$deploy$SparkSubmit$$runMain(SparkSubmit.scala:745)
at org.apache.spark.deploy.SparkSubmit$.doRunMain$1(SparkSubmit.scala:181)
at org.apache.spark.deploy.SparkSubmit$.submit(SparkSubmit.scala:206)
at org.apache.spark.deploy.SparkSubmit$.main(SparkSubmit.scala:121)
at org.apache.spark.deploy.SparkSubmit.main(SparkSubmit.scala)
17/06/27 14:34:42 INFO SparkContext: Invoking stop() from shutdown hook 

这种错误是由于生产环境采用的是scala 2.10 + spark1.6.3的运行环境,本地打的application jar使用scala2.11 + spark.1.6.3的编译环境,所以放入生产环境集群报了上述错误,更改scala版本重新打jar包后运行成功

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • 基于编译虚拟机jvm—openjdk的编译详解

    java只所以被推广,实际上很大原因是因为本身是跨平台的,很大作用是因为虚拟机的关系. 一般情况下开发人员不需要关注虚拟机内部实现就可以日常开发了,但是有时候涉及到性能的时候就需要了解虚拟机的实现机制了. 那么今天写的内容更多的是关于编译一套自己的虚拟机,为日后了解虚拟机底层原理铺铺路. 编译虚拟机可能会遇到很多坑,也很花费时间.也因大家的环境的差异,可能遇到的问题都不一致. 我只能说把自己遇到的问题都列出来,权当抛砖引玉了. 1首先我们应该下载openjdk的源码,这个openjdk实际上是有

  • JDK1.7 Paths,Files类实现文件夹的复制与删除的实例

    实例如下所示: public static void copyFolder(String srcFolder, String destFolder) throws IOException { long startTime = System.currentTimeMillis(); final Path srcPath = Paths.get(srcFolder); // 这里多创建一级,就解决了没有外壳的问题 final Path destPath = Paths.get(destFolder,

  • JDK1.7 之java.nio.file.Files 读取文件仅需一行代码实现

    JDK1.7中引入了新的文件操作类java.nio.file这个包,其中有个Files类它包含了很多有用的方法来操作文件,比如检查文件是否为隐藏文件,或者是检查文件是否为只读文件.开发者还可以使用Files.readAllBytes(Path)方法把整个文件读入内存,此方法返回一个字节数组,还可以把结果传递给String的构造器,以便创建字符串输出.此方法确保了当读入文件的所有字节内容时,无论是否出现IO异常或其它的未检查异常,资源都会关闭.这意味着在读文件到最后的块内容后,无需关闭文件.要注意

  • 详解IntelliJ IDEA 中如何配置多个jdk版本即(1.7和1.8两个jdk都可用)

    有时候需要看Java源码,但是 Java 1.7 和 Java 1.8的差别的关系,有时候你想查看不同jdk版本的Java源码. 或者你的项目需要测试不同Java jdk 版本的通用与否.那么这个编辑器支持多个版本的jdk同时存在,可以自由的切换 具体实现,看下图,一次配置,可以随便使用,省事 安心. 这是选择的是1.8版本的jdk 这是选择的是1.7的版本的jdk 一般像使用eclipse的时候,还得整个环境变量,但是我的这个1.7的时候,好像也没修改环境变量,我的那个测试用 的hellowo

  • JDK反序列化时修改类的全限定性名解析

    应用场景 SpringSecurityOAuth2有一个奇葩的设计,那就是它将与access_token相关的所有属于都封装到OAuth2AccessToken中,然后保存时会直接将该对象序列化成字节写入数据库.我们在资源服务器中想要直接读数据库来取出access_token来验证令牌的有效性,然而又不想引入SpringSecurity的相关依赖污染jar包.这时可以将SpringSecurity中OAuth2AccessToken的唯一实现类DefaultOAuth2AccessToken的源

  • 通过JDK源码角度分析Long类详解

    概况 Java的Long类主要的作用就是对基本类型long进行封装,提供了一些处理long类型的方法,比如long到String类型的转换方法或String类型到long类型的转换方法,当然也包含与其他类型之间的转换方法.除此之外还有一些位相关的操作. Java long数据类型 long数据类型是64位有符号的Java原始数据类型.当对整数的计算结果可能超出int数据类型的范围时使用. long数据类型范围是-9,223,372,036,854,775,808至9,223,372,036,85

  • CentOS 7下JDK8的详细安装步骤

    本文为大家分享了CentOS 7下安装JDK8的详细步骤,供大家参考,具体内容如下 一.下载JDK 至oracle官网下载,如图所示 二.安装JDK 把rpm文件拷贝至/usr/java,如果没有java文件夹, mkdir /usr/java/ 使用npm -ivh jdk文件名安装,如图所示 三.配置环境变量 使用命令 vi /etc/profile,插入图中红色部分: 注意=两边不要有空格 使用:wq保存退出,然后输入source /etc/profile和java -version,如图

  • JDK1.7以上javaFTP上传删除文件的实现方法

    实例如下: packagecom.itv.launcher.util; importjava.io.File; importjava.io.FileInputStream; importjava.io.FileNotFoundException; importjava.io.IOException; importjava.net.InetSocketAddress; importjava.util.Properties; importjava.util.StringTokenizer; impo

  • 如何为Spark Application指定不同的JDK版本详解

    前言 随着企业内部业务系统越来越多,基于JVM的服务,通常情况线上环境可能会有多套JDK跑不同的服务.大家都知道基于高版本的Java规范编写的服务跑在低版本的JVM上会出现:java.lang.UnsupportedClassVersionError的异常. Spark 2.2开始移除了对Java 7的支持,大多数情况下,我们的Spark Application是和Hadoop系统公用的JDK,如果Hadoop依赖的JDK版本是7,那我们基于JDK 8编写的Application跑在上面就会出问

  • Spark GraphX 分布式图处理框架图算法详解

    目录 正文 Graphx图结构 1. 最短路径 示例数据 可视化数据 计算最短路径 2. 网页排名 数据可视化 pagerank算法测试 算法结果 3. 连通域(连通组件) 加载图测试连通域 生成图测试 图实例的形态展示 强连接域的计算 4. 三角计数 代码测试 测试结果 5. 标签传播算法(LPA) 基本思想 正文 Spark GraphX是一个分布式图处理框架,基于 Pregel 接口实现了常用的图算法. 包括 PageRank.SVDPlusPlus.TriangleCount. Conn

  • 使用AngularJS编写多选按钮选中时触发指定方法的指令代码详解

    最近在做项目时,遇到了需要用到多选按钮选中触发事件的功能,因此我查找了一下AngularJS的提供的指令,但是没有发现相应的指令.而一个看起来很像的指令就是ng-checked,但是这个指令是用来代替标签里面checked属性的,所以也用不了.因此我就自己动手试着写一个这样的指令,相应的代码如下: <form name="test_form" ng-controller="TestCtrl"> <input type="checkbox&

  • iOS开发中以application/json上传文件实例详解

    本文通过实例代码给大家讲解iOS中以application/json上传文件的形式,具体内容详情大家参考下本文. 在和sever后台交互的过程中.有时候.他们需要我们iOS开发者以"application/json"形式上传. NSString *accessUrl = [NSString stringWithFormat:@"%@/xxx",@"https://www.xxxxx.com:xxxx"]; NSMutableURLRequest

  • python获取指定时间差的时间实例详解

    python获取指定时间差的时间实例详解 在分析数据的时间经常需要截取一定范围时间的数据,比如三天之内,两小时前等等时间要求的数据,因此将该部分经常需要用到的功能模块化,方便以后以后用到的时候复用.在此,也分享给大家. import time import sys reload(sys) def get_day_of_day(UTC=False, days=0, hours=0, miutes=0, seconds=0): ''''''' if days>=0,date is larger th

  • android 限制某个操作每天只能操作指定的次数(示例代码详解)

    最近有个需求,要求启动页的拦截页每天只能显示3次,超过三次就显示别的页面,然后到第二天才可以再次显示,利用SharePreferences保存天数和每天的次数,大概是思路是:判断 如果是同一天,就去拿保存的次数,当次数小于3才执弹出拦截页,然后,每次弹出,次数就加1,并且保存次数和当天的时间:如果不是同一天,就把次数赋值为1,并且把当天赋值给最后访问的时间,然后保存当前的次数.具体实现如下: package com.example.demo1.test; import android.suppo

  • 基于python的Linux系统指定进程性能监控思路详解

    监控Linux服务器的工具.组件和程序网上有很多,但是一台服务器上会有很多进程同时运行,特别是做性能测试的时候,可能一台服务器上部署多个服务,如果只监控整个服务器的CPU和内存,当某个服务出现性能问题时,并不能有效准确的定位出(当然通过其他工具也可以实现),因此,很有必要只监控指定的进程.需求明确了,于是动手撸了一个性能监控脚本. 一.整体思路 1.为了方便的启动监控和停止监控,在想查看监控结果的时候随时查看监控结果,用flask开启了一个服务,通过发送get请求可以随时启停监控和查看监控结果.

  • iOS开发之如何给View添加指定位置的边框线详解

    前言 本文主要给大家介绍了关于iOS如何给View添加指定位置边框线的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 略微封装了一下,给View添加指定位置的边框线,其中位移枚举的使用询问了哥们儿,总算搞定: 示例代码 封装一:直接封装成了一个方法 /// 边框类型(位移枚举) typedef NS_ENUM(NSInteger, UIBorderSideType) { UIBorderSideTypeAll = 0, UIBorderSideTypeTop = 1

  • CentOS 设置默认JDK步骤详解及命令

     CentOS下设置默认JDK 最近在弄Linux,用yum源安装opnjdk-devel版本后,用命令ll /etc/alternatives/Java查看,发现指向的是jre目录,而不是jdk,在此设置指向jdk目录. CentOS下设置默认JDK 执行命令: [root@exkPicture ~]# alternatives –install /usr/bin/Java java /usr/java/jdk1.7.0/bin/java 2 注意: alternatives的语法看这里 执行

  • python删除指定类型(或非指定)的文件实例详解

    本文实例分析了python删除指定类型(或非指定)的文件用法.分享给大家供大家参考.具体如下: 如下,删除目录下非源码文件 import os import string def del_files(dir,topdown=True): for root, dirs, files in os.walk(dir, topdown): for name in files: pathname = os.path.splitext(os.path.join(root, name)) if (pathna

随机推荐