摘要:本文将介绍如何在 Spark scala 程序中调用 Python 脚本,Spark java程序调用的过程也大体相同。

本文分享自华为云社区《【Spark】如何在Spark Scala/Java应用中调用Python脚本》,作者: 小兔子615 。

1.PythonRunner

对于运行与 JVM 上的程序(即Scala、Java程序),Spark 提供了 PythonRunner 类。只需要调用PythonRunner 的main方法,就可以在Scala或Java程序中调用Python脚本。在实现上,PythonRunner 基于py4j ,通过构造GatewayServer实例让python程序通过本地网络socket来与JVM通信。

// Launch a Py4J gateway server for the process to connect to; this will let it see our
// Java system properties and such
val localhost = InetAddress.getLoopbackAddress()
val gatewayServer = new py4j.GatewayServer.GatewayServerBuilder()
.authToken(secret)
.javaPort(0)
.javaAddress(localhost)
.callbackClient(py4j.GatewayServer.DEFAULT_PYTHON_PORT, localhost, secret)
.build()
val thread = new Thread(new Runnable() {
override def run(): Unit = Utils.logUncaughtExceptions {
gatewayServer.start()
}
})
thread.setName("py4j-gateway-init")
thread.setDaemon(true)
thread.start() // Wait until the gateway server has started, so that we know which port is it bound to.
// `gatewayServer.start()` will start a new thread and run the server code there, after
// initializing the socket, so the thread started above will end as soon as the server is
// ready to serve connections.
thread.join()

在启动GatewayServer后,再通过ProcessBuilder构造子进程执行Python脚本,等待Python脚本执行完成后,根据exitCode判断是否执行成功,若执行失败则抛出异常,最后关闭gatewayServer。

 // Launch Python process
val builder = new ProcessBuilder((Seq(pythonExec, formattedPythonFile) ++ otherArgs).asJava)
try {
val process = builder.start() new RedirectThread(process.getInputStream, System.out, "redirect output").start() val exitCode = process.waitFor()
if (exitCode != 0) {
throw new SparkUserAppException(exitCode)
}
} finally {
gatewayServer.shutdown()
}

2.调用方法

2.1 调用代码

PythonRunner的main方法中需要传入三个参数:

  • pythonFile:执行的python脚本
  • pyFiles:需要添加到PYTHONPATH的其他python脚本
  • otherArgs:传入python脚本的参数数组
val pythonFile = args(0)
val pyFiles = args(1)
val otherArgs = args.slice(2, args.length)

具体样例代码如下,scala样例代码:

package com.huawei.bigdata.spark.examples

import org.apache.spark.deploy.PythonRunner
import org.apache.spark.sql.SparkSession object RunPythonExample {
def main(args: Array[String]) {
val pyFilePath = args(0)
val pyFiles = args(1)
val spark = SparkSession
.builder()
.appName("RunPythonExample")
.getOrCreate() runPython(pyFilePath, pyFiles) spark.stop()
} def runPython(pyFilePath: String, pyFiles :String) : Unit = {
val inputPath = "-i /input"
val outputPath = "-o /output"
PythonRunner.main(Array(pyFilePath, pyFiles, inputPath, outputPath))
}
}

python样例代码:

#!/usr/bin/env python
# coding: utf-8
import sys
import argparse argparser = argparse.ArgumentParser(description="ParserMainEntrance")
argparser.add_argument('--input', '-i', help="input path", default=list(), required=True)
argparser.add_argument('--output', '-o', help="output path", default=list(), required=True)
arglist = argparser.parse_args() def getTargetPath(input_path, output_path):
try:
print("input path: {}".format(input_path))
print("output path: {}".format(output_path))
return True
except Exception as ex:
print("error with: {}".format(ex))
return False if __name__ == "__main__":
ret = getTargetPath(arglist.input, arglist.output)
if ret:
sys.exit(0)
else:
sys.exit(1)

2.2 运行命令

​ 执行python脚本需要设置pythonExec,即执行python脚本所使用的执行环境。默认情况下,使用的执行器为python(Spark 2.4 及以下)或 python3 (Spark 3.0 及以上)。

//Spark 2.4.5
val sparkConf = new SparkConf()
val secret = Utils.createSecret(sparkConf)
val pythonExec = sparkConf.get(PYSPARK_DRIVER_PYTHON)
.orElse(sparkConf.get(PYSPARK_PYTHON))
.orElse(sys.env.get("PYSPARK_DRIVER_PYTHON"))
.orElse(sys.env.get("PYSPARK_PYTHON"))
.getOrElse("python") //Spark 3.1.1
val sparkConf = new SparkConf()
val secret = Utils.createSecret(sparkConf)
val pythonExec = sparkConf.get(PYSPARK_DRIVER_PYTHON)
.orElse(sparkConf.get(PYSPARK_PYTHON))
.orElse(sys.env.get("PYSPARK_DRIVER_PYTHON"))
.orElse(sys.env.get("PYSPARK_PYTHON"))
.getOrElse("python3")

如果要手动指定pythonExec,需要在执行前设置环境变量(无法通过spark-defaults传入)。在cluster模式下,可以通过 --conf “spark.executorEnv.PYSPARK_PYTHON=python3” --conf “spark.yarn.appMasterEnv.PYSPARK_PYTHON=python3” 设置。driver端还可以通过export PYSPARK_PYTHON=python3 设置环境变量。
​ 若需要上传pyhton包,可以通过 --archive python.tar.gz 的方式上传。

​ 为了使应用能够获取到py脚本文件,还需要在启动命令中添加 --file pythonFile.py 将python脚本上传到 yarn 上。

​ 运行命令参考如下:

spark-submit --master yarn --deploy-mode cluster --class com.huawei.bigdata.spark.examples.RunPythonExample --files /usr/local/test.py --conf "spark.executorEnv.PYSPARK_PYTHON=python3" --conf "spark.yarn.appMasterEnv.PYSPARK_PYTHON=python3" /usr/local/test.jar test.py test.py
如果需要使用其他python环境,而非节点上已安装的,可以通过 --archives 上传python压缩包,再通过环境变量指定pythonExec,例如:
spark-submit --master yarn --deploy-mode cluster --class com.huawei.bigdata.spark.examples.RunPythonExam
 

点击关注,第一时间了解华为云新鲜技术~

教你如何在Spark Scala/Java应用中调用Python脚本的更多相关文章

  1. Mac笔记本中是用Idea开发工具在Java项目中调用python脚本遇到的环境变量问题解决

    问题描述: mac笔记本本身会自带几个python版本,比如python2.7版本,我没有改动mac默认的python版本,只是安装了python3.7版本. 使用Pycharm开发Python项目没 ...

  2. Java程序中调用Python脚本的方法

    在程序开发中,有时候需要Java程序中调用相关Python脚本,以下内容记录了先关步骤和可能出现问题的解决办法. 1.在Eclipse中新建Maven工程: 2.pom.xml文件中添加如下依赖包之后 ...

  3. 使用Runtime.getRuntime().exec()在java中调用python脚本

    举例有一个Python脚本叫test.py,现在想要在Java里调用这个脚本.假定这个test.py里面使用了拓展的包,使得pythoninterpreter之类内嵌的编译器无法使用,那么只能采用ja ...

  4. python爬虫简单实现,并在java中调用python脚本,将数据保存在json文件中

    # coding:utf-8 import urllib2 from bs4 import BeautifulSoup import json import sys reload(sys) sys.s ...

  5. 通过Java调用Python脚本

    在进行开发的过程中,偶尔会遇到需要使用Java调用Python脚本的时候,毕竟Python在诸如爬虫,以及科学计算等方面具有天然的优势.最近在工作中遇到需要在Java程序中调用已经写好的Python程 ...

  6. 如何在Java中调用Python代码

    有时候,我们会碰到这样的问题:与A同学合作写代码,A同学只会写Python,而不会Java, 而你只会写Java并不擅长Python,并且发现难以用Java来重写对方的代码,这时,就不得不想方设法“调 ...

  7. 在Java中调用Python代码

    极少数时候,我们会碰到类似这样的问题:与A同学合作写代码, A同学只会写Python,不熟悉Java ,而你只会写Java不擅长Python,并且发现难以用Java来重写对方的代码,这时,就不得不想方 ...

  8. 在Java中调用Python

    写在前面 在微服务架构大行其道的今天,对于将程序进行嵌套调用的做法其实并不可取,甚至显得有些愚蠢.当然,之所以要面对这个问题,或许是因为一些历史原因,或者仅仅是为了简单.恰好我在项目中就遇到了这个问题 ...

  9. java web应用调用python深度学习训练的模型

    之前参见了中国软件杯大赛,在大赛中用到了深度学习的相关算法,也训练了一些简单的模型.项目线上平台是用java编写的web应用程序,而深度学习使用的是python语言,这就涉及到了在java代码中调用p ...

  10. [DEBUG] java中用Runtime调用python 简单程序输出null

    今天需要在java中调用python脚本,首先考虑的是java自带的Runtime 在ubuntu和win10下分别测试,发现win10报错 java源代码 @Test public void tes ...

随机推荐

  1. 18. 从零开始编写一个类nginx工具, 主动式健康检查源码实现

    wmproxy wmproxy将用Rust实现http/https代理, socks5代理, 反向代理, 静态文件服务器,后续将实现websocket代理, 内外网穿透等, 会将实现过程分享出来, 感 ...

  2. 云端golang开发,无需本地配置,能上网就能开发和运行

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 需求 学习golang的时候,需要一个IDE,还需要一 ...

  3. [Python急救站课程]太阳花的绘制

    太阳花的绘制 from turtle import * color('red', 'yellow') # 分别定义填充颜色 begin_fill() while True: forward(200) ...

  4. ALSA Compress-Offload API

    概述 从 ALSA API 的早期开始,它就被定义为支持 PCM,或考虑到了 IEC61937 等固定比特率的载荷.参数和返回值以帧计算是常态,这使得扩展已有的 API 以支持压缩数据流充满挑战. 最 ...

  5. 题解 CF690C1

    题目大意: 给定一张 \(n\) 个点 \(m\) 条边的无向图,判断这是不是一棵树. 题目分析: 两种思路: 思路一: 不需要建图,直接使用并查集判环即可 最后判断一下图联不联通就行,具体方法就是看 ...

  6. 【scipy 基础】--最优化

    SciPy库的optimize模块主要用于执行各种优化任务.优化是寻找特定函数的最小值或最大值的过程,通常用于机器学习.数据分析.工程和其他领域. scipy.optimize提供了多种优化算法,包括 ...

  7. JavaScript Date转字符串格式

    JavaScript Date转字符串格式

  8. Promise规范与原理解析

    摘要 Promise对象用于清晰的处理异步任务的完成,返回最终的结果值,本次分享主要介绍Promise的基本属性以及Promise内部的基础实现,能够帮我们更明确使用场景.更快速定位问题. Promi ...

  9. .NET8极致性能优化Non-GC Heap

    前言 .NET8里面JIT引入了一个新的机制,叫做Non-GC Heap.JIT可以确保相关对象分配在Non-GC Heap上,该堆像其名称一样,不受GC管理.JIT需要保证这个对象没有被GC引用,并 ...

  10. 实现金蝶云星空与赛意SMOM系统的无缝数据对接

    1. 金蝶云星空:为运营协同与管控型企业提供通用ERP服务平台 金蝶云星空是基于当今先进管理理论和数十万家国内客户最佳应用实践开发的ERP服务平台.它针对事业部制.多地点.多工厂等企业和集团公司,提供 ...