如何在Java应用中提交Spark任务?
最近看到有几个Github友关注了Streaming的监控工程——Teddy,所以思来想去还是优化下代码,不能让别人看笑话啊。于是就想改一下之前觉得最丑陋的一个地方——任务提交。
本博客内容基于Spark2.2版本~在阅读文章并想实际操作前,请确保你有:
- 一台配置好Spark和yarn的服务器
- 支持正常
spark-submit --master yarn xxxx的任务提交
老版本
老版本任务提交是基于 ** 启动本地进程,执行脚本spark-submit xxx ** 的方式做的。其中一个关键的问题就是获得提交Spark任务的Application-id,因为这个id是跟任务状态的跟踪有关系的。如果你的资源管理框架用的是yarn,应该知道每个运行的任务都有一个applicaiton_id,这个id的生成规则是:
appplication_时间戳_数字
老版本的spark通过修改SparkConf参数spark.app.id就可以手动指定id,新版本的代码是直接读取的taskBackend中的applicationId()方法,这个方法具体的实现是根据实现类来定的。在yarn中,是通过Yarn的YarnClusterSchedulerBackend实现的,具体的实现逻辑可以参考对应的链接。
感兴趣的同学可以看一下,生成applicaiton_id的逻辑在hadoop-yarn工程的ContainerId中定义。
总结一句话就是,想要自定义id,甭想了!!!!
于是当时脑袋瓜不灵光的我,就想到那就等应用创建好了之后,直接写到数据库里面呗。怎么写呢?
- 我事先生成一个自定义的id,当做参数传递到spark应用里面;
- 等spark初始化后,就可以通过sparkContext取得对应的application_id以及url
- 然后再driver连接数据库,插入一条关联关系

新版本
还是归结于互联网时代的信息大爆炸,我看到群友的聊天,知道了SparkLauncer这个东西,调查后发现他可以基于Java代码自动提交Spark任务。SparkLauncher支持两种模式:
- new SparkLauncher().launch() 直接启动一个Process,效果跟以前一样
- new SparkLauncher().startApplicaiton(监听器) 返回一个SparkAppHandler,并(可选)传入一个监听器
当然是更倾向于第二种啦,因为好处很多:
- 自带输出重定向(Output,Error都有,支持写到文件里面),超级爽的功能
- 可以自定义监听器,当信息或者状态变更时,都能进行操作(对我没啥用)
- 返回的SparkAppHandler支持 暂停、停止、断连、获得AppId、获得State等多种功能,我就想要这个!!!!

一步一步,代码展示
首先创建一个最基本的Spark程序:
import org.apache.spark.sql.SparkSession;
import java.util.ArrayList;
import java.util.List;
public class HelloWorld {
public static void main(String[] args) throws InterruptedException {
SparkSession spark = SparkSession
.builder()
//.master("yarn")
//.appName("hello-wrold")
//.config("spark.some.config.option", "some-value")
.getOrCreate();
List<Person> persons = new ArrayList<>();
persons.add(new Person("zhangsan", 22, "male"));
persons.add(new Person("lisi", 25, "male"));
persons.add(new Person("wangwu", 23, "female"));
spark.createDataFrame(persons, Person.class).show(false);
spark.close();
}
}
然后创建SparkLauncher类:
import org.apache.spark.launcher.SparkAppHandle;
import org.apache.spark.launcher.SparkLauncher;
import java.io.IOException;
public class Launcher {
public static void main(String[] args) throws IOException {
SparkAppHandle handler = new SparkLauncher()
.setAppName("hello-world")
.setSparkHome(args[0])
.setMaster(args[1])
.setConf("spark.driver.memory", "2g")
.setConf("spark.executor.memory", "1g")
.setConf("spark.executor.cores", "3")
.setAppResource("/home/xinghailong/launcher/launcher_test.jar")
.setMainClass("HelloWorld")
.addAppArgs("I come from Launcher")
.setDeployMode("cluster")
.startApplication(new SparkAppHandle.Listener(){
@Override
public void stateChanged(SparkAppHandle handle) {
System.out.println("********** state changed **********");
}
@Override
public void infoChanged(SparkAppHandle handle) {
System.out.println("********** info changed **********");
}
});
while(!"FINISHED".equalsIgnoreCase(handler.getState().toString()) && !"FAILED".equalsIgnoreCase(handler.getState().toString())){
System.out.println("id "+handler.getAppId());
System.out.println("state "+handler.getState());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
然后打包工程,打包过程可以参考之前的博客:
http://www.cnblogs.com/xing901022/p/7891867.html
打包完成后上传到部署Spark的服务器上。由于SparkLauncher所在的类引用了SparkLauncher,所以还需要把这个jar也上传到服务器上。
[xinghailong@hnode10 launcher]$ ls
launcher_test.jar spark-launcher_2.11-2.2.0.jar
[xinghailong@hnode10 launcher]$ pwd
/home/xinghailong/launcher
由于SparkLauncher需要指定SPARK_HOME,因此如果你的机器可以执行spark-submit,那么就看一下spark-submit里面,SPARK_HOME是在哪
[xinghailong@hnode10 launcher]$ which spark2-submit
/var/lib/hadoop-hdfs/bin/spark2-submit
最后几行就能看到:
export SPARK2_HOME=/var/lib/hadoop-hdfs/app/spark
# disable randomized hash for string in Python 3.3+
export PYTHONHASHSEED=0
exec "${SPARK2_HOME}"/bin/spark-class org.apache.spark.deploy.SparkSubmit "$@"
综上,我们需要的是:
- 一个自定义的Jar,里面包含spark应用和SparkLauncher类
- 一个SparkLauncher的jar,spark-launcher_2.11-2.2.0.jar 版本根据你自己的来就行
- 一个当前目录的路径
- 一个SARK_HOME环境变量指定的目录
然后执行命令启动测试:
java -Djava.ext.dirs=/home/xinghailong/launcher -cp launcher_test.jar Launcher /var/lib/hadoop-hdfs/app/spark yarn
说明:
-Djava.ext.dirs设置当前目录为java类加载的目录- 传入两个参数,一个是SPARK_HOME;一个是启动模式
观察删除发现成功启动运行了:
id null
state UNKNOWN
Mar 10, 2018 12:00:52 PM org.apache.spark.launcher.OutputRedirector redirect
INFO: 18/03/10 12:00:52 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
********** state changed **********
...省略一大堆拷贝jar的日志
********** info changed **********
********** state changed **********
Mar 10, 2018 12:00:55 PM org.apache.spark.launcher.OutputRedirector redirect
INFO: 18/03/10 12:00:55 INFO yarn.Client: Application report for application_1518263195995_37615 (state: ACCEPTED)
... 省略一堆重定向的日志
application_1518263195995_37615 (state: ACCEPTED)
id application_1518263195995_37615
state SUBMITTED
Mar 10, 2018 12:01:00 PM org.apache.spark.launcher.OutputRedirector redirect
INFO: 18/03/10 12:01:00 INFO yarn.Client: Application report for application_1518263195995_37615 (state: RUNNING)
********** state changed **********
... 省略一堆重定向的日志
INFO: user: hdfs
********** state changed **********
Mar 10, 2018 12:01:08 PM org.apache.spark.launcher.OutputRedirector redirect
INFO: 18/03/10 12:01:08 INFO util.ShutdownHookManager: Shutdown hook called
Mar 10, 2018 12:01:08 PM org.apache.spark.launcher.OutputRedirector redirect
INFO: 18/03/10 12:01:08 INFO util.ShutdownHookManager: Deleting directory /tmp/spark-f07e0213-61fa-4710-90f5-2fd2030e0701
总结
这样就实现了基于Java应用提交Spark任务,并获得其Appliation_id和状态进行定位跟踪的需求了。
如何在Java应用中提交Spark任务?的更多相关文章
- 如何在java程序中调用linux命令或者shell脚本
转自:http://blog.sina.com.cn/s/blog_6433391301019bpn.html 在java程序中如何调用linux的命令?如何调用shell脚本呢? 这里不得不提到ja ...
- 如何在Java 8中愉快地处理日期和时间
Java 8新增了LocalDate和LocalTime接口,为什么要搞一套全新的处理日期和时间的API?因为旧的java.util.Date实在是太难用了. java.util.Date月份从0开始 ...
- 如何在java项目中使用lucene
lucene是一个开源的全文检索引擎工具包,但它不是一个成型的搜索引擎,它的功能就是负责将文本数据按照某种分词算法进行分词,分词后的结果存储在索引库中,然后根据关键字从索引库检检索. 那么应该如何使用 ...
- 如何在java代码中调用一个web项目jsp或者servlet
有时候需要调用一个web项目的jsp或者servlet,但是执行内部的代码,并不是打开jsp,例如需要在一段java代码中清除一个web项目中的缓存,那么可以把清除缓存的代码放在该web项目的一个se ...
- Cloudera集群中提交Spark任务出现java.lang.NoSuchMethodError: org.apache.hadoop.hbase.HTableDescriptor.addFamily错误解决
Cloudera及相关的组件版本 Cloudera: 5.7.0 Hbase: 1.20 Hadoop: 2.6.0 ZooKeeper: 3.4.5 就算是引用了相应的组件依赖,依然是报一样的错误! ...
- 在Java Web中使用Spark MLlib训练的模型
PMML是一种通用的配置文件,只要遵循标准的配置文件,就可以在Spark中训练机器学习模型,然后再web接口端去使用.目前应用最广的就是基于Jpmml来加载模型在javaweb中应用,这样就可以实现跨 ...
- java操作linux 提交spark jar
maven依赖 <!--Java ssh-2 --><dependency> <groupId>ch.ethz.ganymed</groupId> &l ...
- 如何在Java Filter 中注入 Service
在项目中遇到一个问题,在 Filter中注入 Serivce失败,注入的service始终为null.如下所示: public class WeiXinFilter implements Filter ...
- Android如何在java代码中设置margin
习惯了直接在xml里设置margin(距离上下左右都是10dip),如: <ImageView android:layout_margin="10dip" android:s ...
随机推荐
- DNA序列局部比对(Smith–Waterman algorithm)
生物信息原理作业第三弹:DNA序列局部比对,利用Smith–Waterman算法,python3.6代码实现. 实例以及原理均来自https://en.wikipedia.org/wiki/Smith ...
- Windows 动态链接库编程
Windows 动态链接库编程 1.介绍Windows操作系统是应用最关的操作系统,因此动态链接库也为程序员所熟悉,即使对于普通的使用者来说,很多时候也会碰到.dll结尾的文件,这就是动态链接库文件 ...
- 聊聊一直困扰前端程序员的浏览器兼容-【css】
1.为什么会出现浏览器兼容问题? 由于各大主流浏览器由不同的厂家开发,所用的核心架构和代码也很难重和,这就为各种莫名其妙的Bug(代码错误)提供了温床.再加上各大厂商出于自身利益考虑而设置的种种技术壁 ...
- socketlog的安装和使用
socketlog的使用范围 socketlog比较适用于ajax调试和api的调试,经典应用莫不过于微信众多的api使用调试,使用socketlog可以很条理清楚的查看到api传递的参数,加载的性 ...
- PLEC-交流电机系统+笔记
1.固有机械特性近似图 2.三相交流电机的控制系统 1)理论推导 第一次制动选择能耗制动,第二次制动选择倒拉制动. 2)模型搭建 3)模拟仿真 3.心得体会和笔记总结 制动方式的选择主要是根据各个制动 ...
- Apollo学习
Apollo的配置 参考: Apollo 配置详细步骤(Windows环境) MQTT再学习 -- 搭建MQTT服务器及测试 分布式部署指南 Java客户端使用指南
- ★MySQL一些很重要的SQL语句
[mysqldumpslow] -s 排序选项:c 查询次数 r 返回记录行数 t 查询时间 -t 只显示top n条查询 mysqldumpslow -s r -t 10000 slow-que ...
- 拥抱.NET Core系列:MemoryCache 初识
Cache是一个绝大多数项目会用到的一个技术,说起到缓存可能就联想到 Set.Add.Get.Remove.Clear 这几个方法.那么在.NET Core中微软给我们带来了什么样的缓存体验呢?今天我 ...
- 批量修改git仓库地址脚本
前言 公司的代码都存放在自己搭建的gitlab上面.之前由于老板升级gitlab.导致下面有个叫做"api"的groups无法访问.通过无所不能的谷歌才知道.在gitlab在某 ...
- Action里面的自带的字段的含义