0%

gRPC-Python_server-Java_client

1.配置环境

先换源,避免下载速度过慢或者超时。

永久换源改配置文件时注意权限问题,可以添加 --user 参数将配置保存到用户目录下的 .pip 文件夹中。

1
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple --user

临时换源下载

1
pip install -i  https://pypi.tuna.tsinghua.edu.cn/simple grpc grpc-tools protobuf
2.项目架构
image-20250227035836834

部分功能如果直接在后端服务器上实现会占用比较多的资源,所以实现远程调用,这样性能的主要瓶颈的就是网络IO了。

下面以pdf转png功能为例。

3.pdf转png功能远程调用案例(python服务端实现)

为什么使用python呢,主要是学校另外一个项目使用python做大模型配套功能的实现,这些功能包括各种格式之间的相互转换、数据处理、各种加解密功能、对各种平台的接口的调用、以及对一些大模型平台api的调用,和最后自己这边调好的大模型的调用。(原本用java搭了一套,但是甲方那边要求python搞,应该是python搞数据计算这块工具比较多,所以就换python了)。

在项目目录下创建 format_converter.proto 文件:

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

package format_converter;

// 请求消息,包含 PDF 文件的二进制数据
message ConvertRequest {
int32 req_type = 1;
bytes req_data = 2;
}

// 响应消息,包含转换后的 PNG 图片的二进制数据 ,后续可能是其他类型的数据
message ConvertResponse {
int32 res_type = 1;
repeated bytes res_data = 2; // PDF的每一页转换为一张PNG图片
}

// 定义服务
service FormatConverterService {
rpc ConvertPdfToPng(ConvertRequest) returns (ConvertResponse);
}

安装PyMuPDF库,生成 Python的gRPC代码

1
2
pip install PyMuPDF
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. format_converter.proto

实现服务端逻辑,使用PyMuPDF将pdf转为图片

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
import grpc
from concurrent import futures
import format_converter_pb2
import format_converter_pb2_grpc
import io
import fitz # PyMuPDF

# 实现服务
class FormatConverterService(format_converter_pb2_grpc.FormatConverterServiceServicer):
def ConvertPdfToPng(self, request, context):
# 使用 PyMuPDF 将 PDF 转换为 PNG 图片
pdf_document = fitz.open(stream=request.req_data, filetype="pdf")
response_data = []

for page_num in range(len(pdf_document)):
page = pdf_document.load_page(page_num)
pix = page.get_pixmap() # 获取页面的像素图
png_bytes = pix.tobytes(output="png") # 转换为 PNG 格式
response_data.append(png_bytes)

return format_converter_pb2.ConvertResponse(res_type=1, res_data=response_data)

# 启动服务器
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
format_converter_pb2_grpc.add_FormatConverterServiceServicer_to_server(FormatConverterService(), server)
server.add_insecure_port('[::]:10086')
server.start()
print("Server started on port 10086")
server.wait_for_termination()

if __name__ == '__main__':
serve()
4.pdf转png功能远程调用案例(java客户端实现)

安装依赖和插件

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
<dependencies>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<version>1.45.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>1.45.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>1.45.0</version>
</dependency>
<dependency> <!-- necessary for Java 9+ -->
<groupId>org.apache.tomcat</groupId>
<artifactId>annotations-api</artifactId>
<version>6.0.53</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.21.0</version>
</dependency>
</dependencies>

<build>
<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.21.0:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.45.0:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

maven complie生成java的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
package org.example;

import com.google.protobuf.ByteString;
import format_converter.FormatConverterServiceGrpc;
import format_converter.FormatConverter.ConvertRequest;
import format_converter.FormatConverter.ConvertResponse;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class client {
public static void main(String[] args) throws IOException {
ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 10086)
.usePlaintext()
.build();

FormatConverterServiceGrpc.FormatConverterServiceBlockingStub stub =
FormatConverterServiceGrpc.newBlockingStub(channel);

// 读取 PDF 文件
File pdfFile = new File("merge.pdf");
FileInputStream fis = new FileInputStream(pdfFile);
byte[] pdfData = fis.readAllBytes();
fis.close();

// 创建请求
ConvertRequest request = ConvertRequest.newBuilder()
.setReqType(1) // 假设请求类型为 1 表示 PDF 转 PNG
.setReqData(ByteString.copyFrom(pdfData))
.build();

// 发送请求并获取响应
ConvertResponse response = stub.convertPdfToPng(request);

// 保存 PNG 图片
for (int i = 0; i < response.getResDataCount(); i++) {
FileOutputStream fos = new FileOutputStream("output_" + i + ".png");
fos.write(response.getResData(i).toByteArray());
fos.close();
}

System.out.println("PDF 转 PNG 完成!");
channel.shutdown();
}
}
5.案例结果
image-20250227035857554image-20250227035909667
6.服务端拦截器

服务端使用拦截器拦截验证客户端添加的元数据

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
class AuthInterceptor(grpc.ServerInterceptor):
def __init__(self, key):
self.key = key

def intercept_service(self, continuation, handler_call_details):
# 获取请求的元数据
metadata = handler_call_details.invocation_metadata
if metadata:
for key, value in metadata:
if key == "authorization":
if value == self.key:
return continuation(handler_call_details) # 验证通过,继续处理请求
# 验证失败,返回错误
return grpc.unary_unary_rpc_method_handler(
lambda request: format_converter_pb2.ConvertResponse(
res_type=-1, res_data=[b"Unauthorized"]
),
request_deserializer=format_converter_pb2.ConvertRequest.FromString,
response_serializer=format_converter_pb2.ConvertResponse.SerializeToString,
)
....

interceptor = AuthInterceptor(key="my_secret_key") # 设置验证密钥
server = grpc.server(
futures.ThreadPoolExecutor(max_workers=10),
interceptors=(interceptor,) # 将拦截器传递给服务器 省略了拦截日志
)

客户端在发送请求时添加验证信息

1
2
3
Metadata metadata = new Metadata();
metadata.put(Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER), "my_secret_key");
stub = MetadataUtils.attachHeaders(stub, metadata);