Java跨语言通信之Python

标签: 无 分类: 未分类 创建时间:2020-08-05 05:51:53 更新时间:2024-11-23 02:04:25

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
2
3
pip install grpcio
# 安装 python 下的 protoc 编译器
pip install grpcio-tools

(2)编写 .proto 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
syntax = "proto3";

package proto;

service Message {
// send message
rpc sendMessage (MessageRequest) returns (MessageReply) {}
}
// message request
message MessageRequest {
string message = 1;
}
// message response
message MessageReply {
string message = 1;
int32 code=2;
string result=3;
bool success=4;
}

(3)编译文件
编译 .proto 文件,生成java的文件。

1
2
# 编译 proto 文件
python -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I. message.proto

(4)修改模块导入
修改 src\proto\message_pb2_grpc.py 里面的模块导入方式。

1
2
# import message_pb2 as message_pb2
from . import message_pb2 as message__pb2

(5)编写main.py启动服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
from concurrent import futures
import time
import grpc

# 实现 proto 文件中定义的 message
import proto.message_pb2_grpc as message_pb2_grpc
import proto.message_pb2 as message_pb2

_ONE_DAY_IN_SECONDS = 60 * 60 * 24

# 定义服务
class MessageServer(message_pb2_grpc.MessageServicer):
def sendMessage(self, request, context):
print("接收到客户端消息:" + request.message)

# 服务端向客户端返回多个消息
# for i in message_list:
# yield message_pb2.MessageReply(message= request.message)

return message_pb2.MessageReply(message= request.message)


def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
message_pb2_grpc.add_MessageServicer_to_server(MessageServer(), server)
server.add_insecure_port('[::]:50052')
print("启动服务器等待连接。。。。")
server.start()
server.wait_for_termination()
# try:
# while True:
# time.sleep(_ONE_DAY_IN_SECONDS)
# except KeyboardInterrupt:
# server.stop(0)

if __name__ == '__main__':
serve()

这里使用的是单流的模式,其实如果要是想要服务端向客户端发送多个消息,可以 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<properties>
<!--protobuf 版本号-->
<protobuf.version>3.6.0</protobuf.version>
<!--grpc版本号-->
<grpc.version>1.7.0</grpc.version>
</properties>

<dependencies>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<version>1.26.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>1.26.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>1.26.0</version>
</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.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>16</source>
<target>16</target>
</configuration>
</plugin>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.5.0</version>
<configuration>
<!--默认的proto文件路径-->
<protoSourceRoot>src/main/proto</protoSourceRoot>
<!--Protobuf compiler artifact specification, in groupId:artifactId:version[:type[:classifier]] format. When this parameter is set, the plugin attempts to resolve the specified artifact as protoc executable.-->
<protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}
</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}
</pluginArtifact>
<!--设置是否在生成java文件之前清空outputDirectory的文件,默认值为true,设置为false时也会覆盖同名文件-->
<clearOutputDirectory>true</clearOutputDirectory>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal> <!--按照指定的插件进行编译,即按照GRPC协议编译protob文件-->
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

(2)编写 proto 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
syntax = "proto3";

option java_multiple_files = true;
package proto;

service Message {
// send message
rpc sendMessage (MessageRequest) returns (MessageReply) {}
}
// message request
message MessageRequest {
string message = 1;
}
// message response
message MessageReply {
string message = 1;
int32 code=2;
string result=3;
bool success=4;
}

(3)使用protobuf插件生成Java代码
执行编译命令进行编译,没有问题的话,最后生成的代码会到 target/generated-sources/protobuf 目录下,就可以使用了。

1
$ mvn compile

将生成的 java/proto 和 grpc-java/proto 目录里面的文件,拷贝到 相应的包目录下等待使用。

(4)编写客户端
编写客户端连接,如下代码,注意这里的 com.dji.sample.ai.grpc 包,其实和你自己的包有关系,需要改成你自己的包名,比如我这里放到的就是 grpc 这个目录下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import com.dji.sample.ai.grpc.MessageGrpc;
import com.dji.sample.ai.grpc.MessageReply;
import com.dji.sample.ai.grpc.MessageRequest;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.StatusRuntimeException;

import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

public class MessageClient {
private static final Logger logger = Logger.getLogger(MessageClient.class.getName());

private final ManagedChannel channel;
private final MessageGrpc.MessageBlockingStub blockingStub;

/**
* Construct client connecting to HelloWorld server at {@code host:port}.
*/
public MessageClient(String host, int port) {
this(ManagedChannelBuilder.forAddress(host, port)
// Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid
// needing certificates.
.usePlaintext()
.build());
}

/**
* Construct client for accessing HelloWorld server using the existing channel.
*/
MessageClient(ManagedChannel channel) {
this.channel = channel;
blockingStub = MessageGrpc.newBlockingStub(channel);
}

public void shutdown() throws InterruptedException {
channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
}

/**
* Say hello to server.
*/
public void sendMessage(String message) {
MessageRequest request = MessageRequest.newBuilder().setMessage(message).build();
MessageReply response;
try {
response = blockingStub.sendMessage(request);
logger.info("接收来自服务器的响应: " + response.getMessage());
} catch (StatusRuntimeException e) {
logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
return;
}

}

/**
* Greet server. If provided, the first element of {@code args} is the name to use in the
* greeting.
*/
public static void main(String[] args) throws Exception {
// Access a service running on the local machine on port 50052
MessageClient client = new MessageClient("localhost", 50052);
try {
String user = "world";
// Use the arg as the name to greet if provided
if (args.length > 0) {
user = args[0];
}
int i = 0 ;
while (true){
client.sendMessage(user+i++);
Thread.sleep(1000);
}
} finally {
client.shutdown();
}
}
}

(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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import grpc
import first_pb2 as pb2
import first_pb2_grpc as pb2_grpc

def run():
# 定义一个频道
conn = grpc.insecure_channel('127.0.0.1:5000')
# 生成客户端
client = pb2_grpc.BibiliStub(channel=conn)
response = client.HelloDewei(pb2.HellDeweiReq(
name='wgs',
age=25
))
print(response.result)

if __name__ == '__main__':
run()

参考文章:
【1】.python grpc 服务端和客户端调用demo
【2】.python grpc使用示例 这是客户端 grpc 的完整例子。

3.4.Java服务端

使用 java 作为grpc的服务端,主要的步骤如下:

(1)编写 proto 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
syntax = "proto3";

option java_multiple_files = true;
package proto;

service ManageAi {
// 增加AI
rpc addManageAi(ManageAiRequest) returns (ManageAiResponse) {}
}

message ManageAiRequest {
// 任务id
string job_id=1;
// 任务名称
string job_name=2;
// 唯一id
string uuid=3;
// 时间
string time=5;
// 图片
string photo=6;
// 视频
string video=7;
}

message ManageAiResponse {
string message = 1;
int32 code=2;
string result=3;
bool success=4;
}

(2)添加 gRPC 依赖和插件

(3)生成存根
就是生成 grpc 的java代码

(4)实现Service
创建一个新的 AiServer 类来实现 addManageAi 操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Slf4j
public class AiServer extends ManageAiGrpc.ManageAiImplBase {
// 增加AI识别记录
@Override
public void addManageAi(ManageAiRequest request, StreamObserver<ManageAiResponse> responseObserver) {
ManageAiResponse response = ManageAiResponse.newBuilder()
.setCode(200)
.setSuccess(true)
.build();

// Use responseObserver to send a single response back
responseObserver.onNext(response);

// When you are done, you must call onCompleted.
responseObserver.onCompleted();
}
}

这里是单流,也就是客户端和服务端都是一次请求和一次响应,其实也可以改成从服务端向客户端流式传输,只要多次调用 onNext(response) 返回多个响应就好了,最后再掉用 onCompleted 方法即可。

(5)启动服务器
实现服务器监听

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class App
{
public static void main( String[] args ) throws Exception
{
// Create a new server to listen on port 8080
Server server = ServerBuilder.forPort(8080)
.addService(new GreetingServiceImpl())
.build();

// Start the server
server.start();

// Server threads are running in the background.
System.out.println("Server started");
// Don't exit the main thread. Wait until server is terminated.
server.awaitTermination();
}
}

(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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
syntax = "proto3";

option java_multiple_files = true;
package proto;

service ManageAi {
// 文件上传
rpc fileUpload(stream FileUploadRequest) returns (ManageAiResponse) {}
}

message FileUploadRequest {
oneof data {
string name = 1;
bytes chunk = 2;
}
}

message ManageAiResponse {
string message = 1;
int32 code=2;
string result=3;
bool success=4;
}

(2) 生成java代码
根据proto文件,生成java代码,将生成的target下的generated-soueces/protobuf下的 java 代码复制到项目中,待使用。

1
mvn compile

(3) 实现Service端
这里主要就是注意,要在 fileUpload 方法中,返回一个 StreamObserver 操作,其中的 onNext 方法负责接收客户端传入的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@Slf4j
public class AiServer extends ManageAiGrpc.ManageAiImplBase {
// 上传接口
@Override
public StreamObserver<FileUploadRequest> fileUpload(StreamObserver<ManageAiResponse> responseObserver){
return new StreamObserver<FileUploadRequest>() {

// 实现数据接收
@Override
public void onNext(FileUploadRequest stock) {
log.info(stock.toString());
}

// 数据接收完成,返回信息
@Override
public void onCompleted() {
responseObserver.onNext(ManageAiResponse.newBuilder()
.build());
responseObserver.onCompleted();
}
// 数据接收错误
@Override
public void onError(Throwable t) {
log.error(t.getMessage(), t);
}
};
}

public static void main( String[] args ) throws Exception
{
// Create a new server to listen on port 8080
Server server = ServerBuilder.forPort(9999)
.addService(new AiServer())
.build();

// Start the server
server.start();

// Server threads are running in the background.
System.out.println("Server started");
// Don't exit the main thread. Wait until server is terminated.
server.awaitTermination();
}
}

(4) 生成python代码
根据 proto 文件,执行生成 python 代码。

1
python -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I. ManageAi.proto

(5) 实现clint端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#!/usr/bin/python
# -*- coding: UTF-8 -*-

import logging
import os
import grpc
# 客户端
import proto.ManageAi_pb2 as ManageAI_pb2
import proto.ManageAi_pb2_grpc as ManageAi_pb2_grpc

def fileUpload():
yield ManageAI_pb2.FileUploadRequest(name=os.path.basename("秸秆1"))
with open(r'D:\\zlc\\drone\\视频\\秸秆1.mp4', 'rb') as f:
size = os.path.getsize(r'D:\\zlc\\drone\\视频\\秸秆1.mp4') / 1024
n = 0
while True:
content = f.read(1024)
if content:
n = n + 1
print('传输进度:{}%'.format(round(n / size * 100, 2)))
yield ManageAI_pb2.FileUploadRequest(chunk=content)
else:
break


def client():
with grpc.insecure_channel('localhost:9999') as channel:
client = ManageAi_pb2_grpc.ManageAiStub(channel) # 客户端使用Stub类发送请求,参数为频道,为了绑定链接
response = client.fileUpload(fileUpload()) # 需要将上面的send_stream传进来
print('返回结果:', response.message)

if __name__ == '__main__':
client()
参考文章:
【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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
syntax = "proto3";
#在Python时候不需要这个选项
#option java_multiple_files = true;
package shiyun;

// The greeting service definition.
service AutoChat {
// Sends a greeting
rpc autoChat (AutoChatRequest) returns (AutoChatReply) {}
}

// The request message containing the user's name.
message AutoChatRequest {
string question = 1;
}

// The response message containing the greetings
message AutoChatReply {
string response = 1;
}

【解决方案】
上面的 .proto 的内容有些问题:

  • 不能包含中文
  • 注释应该为 //, 而不是 #

正确的 .proto 文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
syntax = "proto3";
package shiyun;
// The greeting service definition.
service AutoChat {
rpc autoChat (AutoChatRequest) returns (AutoChatReply) {}
}
// The request message containing the user's name.
message AutoChatRequest {
string question = 1;
}
// The response message containing the greetings
message AutoChatReply {
string response = 1;
}
参考文章:
【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.
小额赞助
本人提供免费与付费咨询服务,感谢您的支持!赞助请发邮件通知,方便公布您的善意!
**光 3.01 元
Sun 3.00 元
bibichuan 3.00 元
微信公众号
广告位
诚心邀请广大金主爸爸洽谈合作
每日一省
isNaN 和 Number.isNaN 函数的区别?

1.函数 isNaN 接收参数后,会尝试将这个参数转换为数值,任何不能被转换为数值的的值都会返回 true,因此非数字值传入也会返回 true ,会影响 NaN 的判断。

2.函数 Number.isNaN 会首先判断传入参数是否为数字,如果是数字再继续判断是否为 NaN ,不会进行数据类型的转换,这种方法对于 NaN 的判断更为准确。

每日二省
为什么0.1+0.2 ! == 0.3,如何让其相等?

一个直接的解决方法就是设置一个误差范围,通常称为“机器精度”。对JavaScript来说,这个值通常为2-52,在ES6中,提供了Number.EPSILON属性,而它的值就是2-52,只要判断0.1+0.2-0.3是否小于Number.EPSILON,如果小于,就可以判断为0.1+0.2 ===0.3。

每日三省
== 操作符的强制类型转换规则?

1.首先会判断两者类型是否**相同,**相同的话就比较两者的大小。

2.类型不相同的话,就会进行类型转换。

3.会先判断是否在对比 null 和 undefined,是的话就会返回 true。

4.判断两者类型是否为 string 和 number,是的话就会将字符串转换为 number。

5.判断其中一方是否为 boolean,是的话就会把 boolean 转为 number 再进行判断。

6.判断其中一方是否为 object 且另一方为 string、number 或者 symbol,是的话就会把 object 转为原始类型再进行判断。

每日英语
Happiness is time precipitation, smile is the lonely sad.
幸福是年华的沉淀,微笑是寂寞的悲伤。