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.项目架构 部分功能如果直接在后端服务器上实现会占用比较多的资源,所以实现远程调用,这样性能的主要瓶颈的就是网络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;message ConvertRequest { int32 req_type = 1 ; bytes req_data = 2 ; } message ConvertResponse { int32 res_type = 1 ; repeated bytes res_data = 2 ; } 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 grpcfrom concurrent import futuresimport format_converter_pb2import format_converter_pb2_grpcimport ioimport fitz class FormatConverterService (format_converter_pb2_grpc.FormatConverterServiceServicer): def ConvertPdfToPng (self, request, context ): 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" ) 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 > <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); File pdfFile = new File ("merge.pdf" ); FileInputStream fis = new FileInputStream (pdfFile); byte [] pdfData = fis.readAllBytes(); fis.close(); ConvertRequest request = ConvertRequest.newBuilder() .setReqType(1 ) .setReqData(ByteString.copyFrom(pdfData)) .build(); ConvertResponse response = stub.convertPdfToPng(request); 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.案例结果 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);