Java跨语言通信之Python
1.Java调用Python
有这么一个命题,就是说后端算法部门,使用了TensorFlow等机器学习算法进行了数据预测,现在有个需求,就是说要通过浏览器页面输入相关的计算参数,然后返回计算结果,理论上呢,python也可以作为一个web服务器来开发,但是算法部门的同事不会相关的技术。我后台语言使用的是java,那么如何通过java调用python进行计算呢?或者说,如何把python web集成到java的微服务框架中呢?python和java如何更好的交互,带着这样的问题,开始了这篇文章的探索。
主要有三种方式:
(1) 使用java的库JPython,直接执行python语言,但是Jpython也有缺点
不支持 Python3.0 以上的语法
python 源码中若引用的第三方库包含 C 语言扩展,将无法提供支持,如 numpy 等
(2) 使用Python作为微服务,提供web响应,实现起来简单。但是效率比较低,成本比较高。可以使用Tornado,还有Flask、Django等Python Web服务器。
(3) 将Python使用CPython库转换为c代码,然后使用JNI进行调用。虽然效率高,但是实现的步骤相当复杂,还不一定能成。
(4) 后来我又发现了一种,其实和第二种比较像,只不过不是作为web应用,而是作为一个socket服务来用罢了。java和python之间通过socket进行通讯,这样比起一个笨重的web服务器来说,是不是显得更加的简单呢?
【1】.Java调用Python程序方法总结 (通过jython这个库直接执行python语句)
【2】.Python写的微服务如何融入Spring Cloud体系? (微服务的概念突破了语言的限制,所以也可以通过Python实现微服务,并融入到Java的生态中去。这篇文章使用了Tornado作为python的服务器实现,使用consulate进行服务注册于发现,Feign作为注册和发现中心)
【3】.nacos-sdk-python (nacos的python支持)
【4】.Python 微服务开发–Python Microservices Development (使用python开放微服务)
【5】.Java 调用 python 的接口 (也是使用了jython依赖)
【6】.Python一键转Jar包,Java调用Python新姿势! (这篇文章就如何使用java与python的原因的有关方法做了总结,主要原因是问题解决方向不同,可以通过restful服务进行对接,也可以使用JPython,还可以使用JNI调用,这篇文章就是主要讲了如何将python转换为c语言,并使用java native进行调用的方法)
【7】.Using NumPy and Cpython with Jython (这是如何使用JPython调用Numpy库的讨论,最后好像也没有讨论出结果来)
【8】.java web应用调用python机器学习训练的模型
【9】.java调用python的几种用法(看这篇就够了) 在java类中直接执行python语句;在java类中直接调用本地python脚本;使用Runtime.getRuntime()执行python脚本文件(推荐)
【10】.Java与Python深度学习模型的交互方法 1.使用Jython。2.使用进程间通信。3.使用gRPC或Thrift。
【11】.java web应用调用python深度学习训练的模型 这里提供了实用socket进行通讯的方法,还提供了相关的代码。 1. java代码可以直接调用python代码,只需要下载相应的jar包就行。2. 将python训练的模型参数保存到文本中,用java代码重现模型的预测算法。3. 使用python进程运行深度学习中训练的模型,在java应用程序中调用python进程提供的服务。
2.Python调用Java
在Python中,我们可以使用JPype
库来实现Python与Java之间的交互。JPype
是一个Python模块,提供了Python与Java之间的接口,方便进行跨语言调用。
3.grpc交互
采用gRPC,有了服务的注册中心,服务切换更新更加轻量化,并且遵循 Netty 的线程分工原则,协议层消息的接收和编解码由Netty 的 I/O(NioEventLoop)线程负责;后续应用层的处理由应用线程负责,防止由于应用处理耗时而阻塞 Netty 的 I/O 线程, 可以通过服务名和方法名调用,直接调用启动的时候注册的服务实例,不需要反射或者JSON编码解码进行调用,性能更优; 不过因为有Netty的线程分工原则,gRPC之间会做频繁的线程切换,如果在一次gRPC调用过程中,做了多次I/O线程到应用线程之间的切换,会导致性能的下降。
【1】.java和python使用grpc交互
【2】.Java客户端调用Python服务端 某些大型web服务一般是采用Spring框架编写的,然而深度学习计算框架TensorFlow是采用Python编写的,那么如何让客户端调用Python服务端的计算资源返回结果呢,一种可以采用传统的HTTP Web Service的方式,将服务端封装为Web服务,客户端直接通过JSON请求进行调用并返回结果;另外一种是通过基于HTTP2协议RPC服务,Python将计算服务注册在注册中心,Java客户端通过端口号在注册中心找到对应的服务后,调用服务端的服务实例。
【3】.拥抱云原生,Java与Python基于gRPC通信 大概就是这么一个情况,公司产品需要一个上万人排班(而且可能是多个上万人进行排班)的功能,但系统是基于java做的后台,公司的算法那工程师使用的是python进行算法实现,故需要进行跨系统支持,毕竟算法运算非常非常吃资源。
【4】.JAVA和Python的GRPC远程调用
【5】.gRPC 基础: Python
3.1.Python服务端
我这里参考了网上的文章,然后修改了部分代码,搞成了自己的,目录结构如下图:
(1) 安装依赖
1 | pip install grpcio |
(2)编写 .proto 文件
1 | syntax = "proto3"; |
(3)编译文件
编译 .proto 文件,生成java的文件。
1 | # 编译 proto 文件 |
(4)修改模块导入
修改 src\proto\message_pb2_grpc.py 里面的模块导入方式。
1 | # import message_pb2 as message_pb2 |
(5)编写main.py启动服务
1 | from concurrent import futures |
这里使用的是单流的模式,其实如果要是想要服务端向客户端发送多个消息,可以 yield 关键字去调用,就可以一直返回消息了。
【1】.【Protobuf速成指南】.proto文件的编写与编译
【2】.Python 中使用 gRPC 1.定义 Protocol Buffers 文件。2.生成 gRPC 代码,生成 example_pb2.py 和 example_pb2_grpc.py 两个文件。3.实现服务器。4.实现客户端。5.运行服务器和客户端。
3.2.Java客户端
使用 java 客户端,也要通过 proto 文件生成java代码,有些使用了 protobuf-maven-plugin 插件,但是我修改了pom,好像也不管用,没有运行成功。
(1)引入依赖
1 | <properties> |
(2)编写 proto 文件
1 | syntax = "proto3"; |
(3)使用protobuf插件生成Java代码
执行编译命令进行编译,没有问题的话,最后生成的代码会到 target/generated-sources/protobuf 目录下,就可以使用了。
1 | $ mvn compile |
将生成的 java/proto 和 grpc-java/proto 目录里面的文件,拷贝到 相应的包目录下等待使用。
(4)编写客户端
编写客户端连接,如下代码,注意这里的 com.dji.sample.ai.grpc 包,其实和你自己的包有关系,需要改成你自己的包名,比如我这里放到的就是 grpc 这个目录下。
1 | import com.dji.sample.ai.grpc.MessageGrpc; |
(5)运行
修改了相关的程序包依赖,就可以直接运行客户端了。
【1】.idea protobuf 插件生成java代码 这篇文章是使用 idea 创建了一个 proto 的代码,用到了 protobuf support插件,但是我没有找到这个插件。在 idea 的 Setting 插件市场中安装 Protobuf Support 插件。在 Protobuf 文件中,点击右键并选择 “Generate” -> “Generate protobuf Java code”。这将生成与 Protobuf 文件中定义的消息和服务对应的 Java 代码。生成的 Java 代码将自动保存在与 Protobuf 文件相同的目录中,使用与 Protobuf 文件的名称相同的包名。
【2】.在idea工具下,使用protobuf自动生成java代码,超详细教程 下载protoc.exe编译器,下载地址:Releases otocolbuffers/protobuf · GitHub,下载完后解压,然后配置环境变量:PROTOCBUF_HOME
【3】.Intellij IDEA中使用Protobuf的正确姿势 一般的做法,是执行protoc命令,依次将.proto文件转成Java类: 不过gRPC官方推荐了一种更优雅的使用姿势,可以通过maven轻松搞定
【4】.Protobuf Support 这里有配置 outputDirectory 生成后的文件存放根路径。idea -> Maven -> Plugins -> protobuf -> 点击 protobuf:compile 就可以生成portobuf java实体类
3.3.Python客户端
(1)grpc安装
(2)定义proto文件
(3)生成存根代码
(4)客户端demo
1 | import grpc |
3.4.Java服务端
使用 java 作为grpc的服务端,主要的步骤如下:
(1)编写 proto 文件
1 | syntax = "proto3"; |
(2)添加 gRPC 依赖和插件
(3)生成存根
就是生成 grpc 的java代码
(4)实现Service
创建一个新的 AiServer 类来实现 addManageAi 操作。
1 |
|
这里是单流,也就是客户端和服务端都是一次请求和一次响应,其实也可以改成从服务端向客户端流式传输,只要多次调用 onNext(response) 返回多个响应就好了,最后再掉用 onCompleted 方法即可。
(5)启动服务器
实现服务器监听
1 | public class App |
(6)启动
【1】.java版gRPC实战之三:服务端流 这里用了 grpc-server-spring-boot-starter 和 build.gradle,而不是maven
【2】.java版gRPC实战之二:服务发布和调用 和上面的路子是一样的,都是用一个工程实验的,使用了 grpc-server-spring-boot-starter。
【3】.使用 Java 构建 gRPC 服务 这是谷歌的一个教程,用了原生的grpc服务,您只需将 proto 文件中的 stream 关键字添加到请求或响应参数中,即可轻松构建流服务。
4.文件上传
【1】.grpc图片传输 python实现 这篇文章其实是将图片转为了base64字符串然后进行传输的,还有从客户端以流的形式进行数据传递,服务端接收消息,并将base64转为了图片。
【2】.c# gRPC服务中 常见proto支持的数据类型 1.基本类型;2.枚举类型;3.重复类型;4.嵌套类型;5.时间类型;6.字典类型;7.Oneof 类型。
【3】.gRPC 对应Java类型
【4】.用Python进行gRPC接口测试(三) 标量值类型与我们在编程语言使用的基本数据类型概念类似,用来携带的数据也大体相同。在python中,这些标量值类型都能找到与之对应的python数据类型,处理起来简单便捷。
【5】.gRPC python实现文件上传,以及使用流式通信上传超大文件 这篇文章使用二进制的形式进行文件上传,可以实现超大的文件上传。如果文件比较大,上传过程中很容易出现丢包现象,而且在1次传送又很占内存,特别是几百M的大文件,服务器侧也无法承受,因此解决思路为:在Client 端将大文件分成多个更小的chunk,如64K, 用streaming方式上传,读1次上传1次。利用protobuf message 的oneof 字段修饰符,在第1个上传请求中,发送文件名,后面的request是文件内容chunk。发送完成,server保存文件后,发送1条response, 其中包含 status, 指明结果。
5.流式传输
这里我用 java 作为流接收端进行测试,我结合了参考文章1和参考文章2进行了测试。
(1) 定义proto文件
1 | syntax = "proto3"; |
(2) 生成java代码
根据proto文件,生成java代码,将生成的target下的generated-soueces/protobuf下的 java 代码复制到项目中,待使用。
1 | mvn compile |
(3) 实现Service端
这里主要就是注意,要在 fileUpload 方法中,返回一个 StreamObserver 操作,其中的 onNext 方法负责接收客户端传入的数据。
1 |
|
(4) 生成python代码
根据 proto 文件,执行生成 python 代码。
1 | python -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I. ManageAi.proto |
(5) 实现clint端
1 | #!/usr/bin/python |
【1】.python对于grpc的简单操作(三) 客户端服务端单流、双流在python中的写法,还提供了一个上传视频流的方法。
【2】.在 Java 中使用 gRPC 进行流式(stream)传输 1.服务器流 RPC:客户端向服务器发送单个请求,并获取回几条它顺序读取的消息。2.客户端流 RPC:客户端向服务器发送一系列消息。客户端等待服务器处理消息并读取返回的响应。3.双向流 RPC:客户端和服务器可以双向发送多条消息。消息的接收顺序与发送顺序相同。但是,服务器或客户端可以选择回复接收到的消息的顺序。
【3】.Java版gRPC的使用之二:服务端流、客户端流和双向流 这里的单流、双流都是用java代码去编写的。
【4】.grpc 传输文件 java 这里感觉写流一半,也没有写如何定义的 grpc 方法,如何保存的数据等等。
【5】.gRPC实战(二、流式传输 Java、Go实现) 分别用java和go语言实现了客户端流、服务端流和双向流的问题。
5.问题
4.1.Interpreting non ascii codepoint
在使用 python 生成 proto 文件的使用,出现了这个问题。
1 | syntax = "proto3"; |
【解决方案】
上面的 .proto 的内容有些问题:
- 不能包含中文
- 注释应该为 //, 而不是 #
正确的 .proto 文件如下:
1 | syntax = "proto3"; |
【1】.linux文件头出现非ascii字符
【2】.[Caffe]:关于 Error parsing text-format 这是protobuf提示的一个错误。它的意思是在用户的prototxt中第xxx行,第xx 列有非。
4.2.程序包io.grpc不存在
【解决方案】
这个问题主要就是我不知道什么时候把依赖 grpc-protobuf 这几个弄掉了,或者是我没有找到地方。
【1】.IDEA搭建gRPC服务 这里有 GrpcClientService 服务,@GrpcClient 注解。
4.3.AutoChatGrpc.java:[111,31] 找不到符号
【解决方案】
主要就是把 outputDirectory 这个配置文件删掉了,叫他自动生成到 target/generated-sources/protobuf 目录下。
【1】.IntelliJ IDEA 中找不到 protocol buffer 生成的 Java 类的解决方法 如果 protoc 生成的 class 文件找不到,说明 protoc 文件没有没有正确编译。 可以调整 IDEA 的默认参数, 然后重启 IntelliJ IDEA 使得 IDE 能够正确找到 protoc 生成的 Java 类。
【2】.idea的java代码引用proto文件报错
4.4.Protobuf gencode version 5.27.2 is older than the runtime version 5.28.2 at ManageAi.proto. Please avoid checked-in Protobuf gencode that can be obsolete.
编写完了客户端,服务端,但是进行测试的时候,客户端出现了这个问题,服务端java出现了。
【解决方案】
这个好像只是一个警告,可以不用管。
【1】.Excessive new warning in Python protobuf runtime
【2】.UserWarnings emitted from all protoc-generated _pb2 packages For me, it was a problem with protobuf. In my requirements.txt, I specified protobuf==5.27.2, and now the warning has disappeared.