java调用python程序

丰富的第三方库使得python非常适合用于进行数据分析,最近在项目中就涉及到java调用python实现的算法。目前,java调用python主要包括三种方法:1、利用runtime在本地运行python脚本;2、通过jython调用;3、java通过rpc(远程过程调用)调用python。其中,runtime调用python实际上是通过cmd命令本地运行python脚本,对传递参数的长度有限制;而通过jython调用的方式无法导入numpy等第三方库。通过rpc远程调用的方式则不存在上述限制,是比较理想的方式。

1、通过runtime调用python

代码如下:

package pythonTest;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class pythonTest {
    public static void main(String[] args) {
        String filePath = ".\\src\\AverageEcho6211701.txt";
        String coePath = ".\\src\\OVlidar20190116.txt";
        String[] data = TxtUtil.getContentByLine(filePath,3000);
        String[] coeData = TxtUtil.getContentByLine(coePath, 500);
        System.out.println(data[0]);
        int bearing = 90;
        double decCoe = 0.25;
        try {
            String[] pythonArgs = new String[] { "python", ".\\src\\11.py", String.valueOf(bearing), String.valueOf(decCoe),
                    String.join(",", data)};
            String[] appendPythonArgs = new String[pythonArgs.length + coeData.length];
            System.arraycopy(pythonArgs, 0, appendPythonArgs, 0, pythonArgs.length);
            System.arraycopy(coeData, 0, appendPythonArgs, pythonArgs.length, coeData.length);
            Process proc = Runtime.getRuntime().exec(pythonArgs);// 执行py文件
            // 定义Python脚本的返回值
            String result = null;
            BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
            String line = null;
            while ((line = in.readLine()) != null) {
                result = line;
            }
            in.close();
            proc.waitFor();
            System.out.println(result);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

通过runtime调用实际上是通过cmd命令在本地运行python脚本,参数以字符串形式进行传递,当字符串超出一定长度时程序会报错。

java.io.IOException: Cannot run program "python": CreateProcess error=206, 文件名或扩展名太长。
    at java.lang.ProcessBuilder.start(ProcessBuilder.java:1048)
    at java.lang.Runtime.exec(Runtime.java:620)
    at java.lang.Runtime.exec(Runtime.java:485)
    at pythonTest.pythonTest.main(pythonTest.java:22)
Caused by: java.io.IOException: CreateProcess error=206, 文件名或扩展名太长。
    at java.lang.ProcessImpl.create(Native Method)
    at java.lang.ProcessImpl.<init>(ProcessImpl.java:386)
    at java.lang.ProcessImpl.start(ProcessImpl.java:137)
    at java.lang.ProcessBuilder.start(ProcessBuilder.java:1029)
    ... 3 more

2、jython

jython是在jvm上实现的python,由java编写,jython将python源码变异成jvm字节码,由jvm执行对应的字节码。使用前需要在pom文件中导入相关的jar包:

<!-- https://mvnrepository.com/artifact/org.python/jython-standalone -->
        <dependency>
            <groupId>org.python</groupId>
            <artifactId>jython-standalone</artifactId>
            <version>2.7.1</version>
        </dependency>

相关代码如下:

package pythonTest;

import org.python.core.*;
import org.python.util.PythonInterpreter;


public class pythonTest {
    public static void main(String[] args) {
        String filePath = ".\\src\\main\\resources\\AverageEcho6211701.txt";
        String coePath = ".\\src\\main\\resources\\OVlidar20190116.txt";
        String[] data = TxtUtil.getContentByLine(filePath,3000);
        String[] coeData = TxtUtil.getContentByLine(coePath, 500);
        System.out.println(data[0]);
        int bearing = 90;
        double decCoe = 0.25;

        PythonInterpreter interpreter = new PythonInterpreter();

        interpreter.exec("import sys");
        interpreter.execfile(".\\src\\main\\python\\11.py");
        // 第一个参数为期望获得的函数(变量)的名字,第二个参数为期望返回的对象类型
        PyFunction pyFunction = interpreter.get("add", PyFunction.class);
        int a = 5, b = 10;
        //调用函数,如果函数需要参数,在Java中必须先将参数转化为对应的“Python类型”
        PyObject pyobj = pyFunction.__call__(new PyInteger(a), new PyInteger(b));
        System.out.println("the anwser is: " + pyobj);


    }
}

当python脚本中需要导入第三方库时程序报错:

Exception in thread "main" Traceback (most recent call last):
  File ".\src\main\python\11.py", line 2, in <module>
    import numpy
ImportError: No module named numpy

3、rpc远程调用python

rpc框架实际提供了一套机制,使得应用程序之间可以进行通信,采用server/client模型,客户端调用服务端接口。目前开源的rpc框架有Dubbo、Thrift、grpc等,其中dubbo仅支持java,thrift与grpc支持多语言,使用grpc实现java调用python程序,java实现客户端,python实现服务端。grpc具有以下特点:1、grpc通过protobuf定义接口,可以将数据序列化成二进制编码,大幅减少需要传输的数据量,从而提高性能;2、grpc支持流式场景。
首先,定义protobuf接口:


syntax = "proto3";

package com.wayealCloud.mlp;

service MlpAlgorithm
{
    rpc mlpInverting(Request) returns (Result){}
}

message Request
{
    string data = 1;
    string coeData = 2;
    string bearing = 3;
    string decCoe = 4;
}

message Result
{
    string exData = 1;
}

在proto文件所在目录下进行编译:

python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. ./mlp.proto

然后,编写python服务端程序,代码如下:

# -*- coding: utf-8 -*-
"""
@author: yhl
@time: 2019/09/16
激光雷达反演算法
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from ETLandTraining.spark_code.micro_pulse_lidar.micro_pulse_lidar import SectionalSlopeAlgorithm, \
    FernaldAlgorithm, WaveletDenoising, move_avg, fanyan_algorithm

from concurrent import futures
from src.ETLandTraining.spark_code.micro_pulse_lidar.src import mlp_pb2_grpc, mlp_pb2

import numpy as np
import math
import matplotlib.pyplot as plt
import time
import grpc

_ONE_DAY_IN_SECONDS = 60 * 60 * 24

_HOST = '192.168.200.234'
IP = "9000"


class ServiceMain(mlp_pb2_grpc.MlpAlgorithmServicer):
    def mlpInverting(self, request, context):
        data = request.data.split(',')
        data = [item.split('  ') for item in data]
        data = np.array(data).astype('float32')
        ovlidar = request.coeData.split(',')
        ovlidar = np.array(ovlidar).astype('float32')
        bearing = int(request.bearing)
        dec_coe = float(request.decCoe)
        p1 = data[:, 1]
        p2 = data[:, 2]
        er, dec_ratio = fanyan_algorithm(bearing, p1, p2, ovlidar, dec_coe)
        return mlp_pb2.Result(exData=str(er[0]))


def serve():
    grpc_server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    mlp_pb2_grpc.add_MlpAlgorithmServicer_to_server(ServiceMain(), grpc_server)
    grpc_server.add_insecure_port("{}:{}".format(_HOST, IP))
    grpc_server.start()
    print("grpc service start")
    try:
        while True:
            time.sleep(_ONE_DAY_IN_SECONDS)
    except KeyboardInterrupt:
        grpc_server.stop(0)


if __name__ == '__main__':

    serve()

最后,编写java客户端程序。第一步,定义protobuf接口文件且必须与python端保持一致;第二步,在pom文件中添加相关的jar包及插件;第三部,编译及编写客户端程序。pom文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.wayeal.cloud.mlp</groupId>
    <artifactId>mlp</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <!-- https://github.com/grpc/grpc-java-->
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-netty-shaded</artifactId>
            <version>1.23.0</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-protobuf</artifactId>
            <version>1.23.0</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-stub</artifactId>
            <version>1.23.0</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>1.6.2</version>
            </extension>
        </extensions>
        <plugins>
            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>0.6.1</version>
                <configuration>
                    <protocArtifact>com.google.protobuf:protoc:3.9.0:exe:${os.detected.classifier}</protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.23.0:exe:${os.detected.classifier}</pluginArtifact>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

客户端程序如下:

package com.wayealCloud.mlp.mlpClient;

import com.wayealCloud.mlp.util.TxtUtil;
import io.grpc.Grpc;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;

import java.util.concurrent.TimeUnit;

public class MlpClient {
    private final ManagedChannel channel;
    private final MlpAlgorithmGrpc.MlpAlgorithmBlockingStub blockingStub;
    public MlpClient(String host,int port){
        channel = ManagedChannelBuilder.forAddress(host,port)
                .usePlaintext()
                .build();
        blockingStub = MlpAlgorithmGrpc.newBlockingStub(channel);
    }
    public void shutdown() throws InterruptedException {
        channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
    }
    public String greet(String data,  String coeData, String bearing, String decCoe){
        Request request = Request.newBuilder()
                .setData(data)
                .setCoeData(coeData)
                .setBearing(bearing)
                .setDecCoe(decCoe)
                .build();
        Result result = blockingStub.mlpInverting(request);
        System.out.println(result.getExData());
        return result.getExData();
    }

    public static void main(String[] args) throws InterruptedException {
        String filePath = ".\\src\\main\\resources\\AverageEcho6211701.txt";
        String coePath = ".\\src\\main\\resources\\OVlidar20190116.txt";
        String[] data = TxtUtil.getContentByLine(filePath,3000);
        String[] coeData = TxtUtil.getContentByLine(coePath, 500);
        String bearing = "90";
        String decCoe = "0.25";

        MlpClient client = new MlpClient(Constants.IP, Constants.port);
        String result = client.greet(String.join(",", data), String.join(",", coeData), bearing, decCoe);
    }
}

结果如下:

0.3565467136205529

综上所述,基于grpc,java实现客户端,python实现服务端,通过远程调用的方式是比较理想的方法。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • 1.调用python程序不需要传参只需获得结果时: 直接新建java文件,然后新建Process对象调用pytho...
    WM8阅读 3,488评论 0 0
  • gRPC 是一个高性能、通用的开源RPC框架,基于HTTP/2协议标准和Protobuf序列化协议开发,支持众多的...
    小波同学阅读 19,406评论 6 19
  • 原文出处:gRPC gRPC分享 概述 gRPC 一开始由 google 开发,是一款语言中立、平台中立、开源的远...
    小波同学阅读 7,190评论 0 18
  • 1> 安装Homebrew 2> 安装Jenkins 此时会提示: 3> 安装上面的提示安装Java 安装完成后再...
    奥个特曼阅读 1,649评论 0 1
  • 那天我去参加小庄的婚礼。晚上,一群人兴致勃勃的簇拥在她们家小院里搓麻将。百无聊赖的我躺在她床上玩游戏消遣,毕...
    Mmawei阅读 291评论 0 0