使用gRPC进行java和python沟通

Java

getStuNo.proto

syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.getstuno";
option java_outer_classname = "GetStuNoProto";
option objc_class_prefix = "GSN";

package getstuno;

service GetStuNoService {
    rpc getMsg (StuNoRequest) returns (StuNoResponse){}
}

message StuNoRequest {
    string name = 1;
}

message StuNoResponse {
    string number = 1;
}

protocol buffers 是一种语言无关、平台无关、可扩展的序列化结构数据的方法,它可用于(数据)通信协议、数据存储。这里使用proto文件用于生成gRPC所需要的框架。生成方式参照https://blog.csdn.net/qq_29319189/article/details/93539198

GetStuNoServer.java

package getstuno;

import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.examples.getstuno.GetStuNoServiceGrpc;
import io.grpc.examples.getstuno.StuNoResponse;
import io.grpc.examples.getstuno.StuNoRequest;
import io.grpc.stub.StreamObserver;
import java.io.IOException;
import java.util.logging.Logger;

/**
 * Server that manages startup/shutdown of a {@code Greeter} server.
 */
public class GetStuNoServer {
    private static final Logger logger = Logger.getLogger(GetStuNoServer.class.getName());

    private Server server;

    private void start() throws IOException {
        /* The port on which the server should run */
        int port = 50051;
        server = ServerBuilder.forPort(port)
                .addService(new GetStuNoServiceImpl())
                .build()
                .start();
        logger.info("Server started, listening on " + port);
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                // Use stderr here since the logger may have been reset by its JVM shutdown hook.
                System.err.println("*** shutting down gRPC server since JVM is shutting down");
                GetStuNoServer.this.stop();
                System.err.println("*** server shut down");
            }
        });
    }

    private void stop() {
        if (server != null) {
            server.shutdown();
        }
    }

    /**
     * Await termination on the main thread since the grpc library uses daemon threads.
     */
    private void blockUntilShutdown() throws InterruptedException {
        if (server != null) {
            server.awaitTermination();
        }
    }

    /**
     * Main launches the server from the command line.
     */
    public static void main(String[] args) throws IOException, InterruptedException {
        final GetStuNoServer server = new GetStuNoServer();
        server.start();
        server.blockUntilShutdown();
    }

    static class GetStuNoServiceImpl extends GetStuNoServiceGrpc.GetStuNoServiceImplBase {

        @Override
        public void getMsg(StuNoRequest req, StreamObserver<StuNoResponse> responseObserver) {
            String number = "0000";
            logger.info("Received name: " + req.getName());
            if (req.getName().equals("爱丽丝")) {
                number = "1234";
            }
            StuNoResponse reply = StuNoResponse.newBuilder().setNumber(number).build();
            responseObserver.onNext(reply);
            responseObserver.onCompleted();
        }
    }
}

Server端重点实现提供的服务

GetStuNoClient.java

package getstuno;

import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.StatusRuntimeException;
import io.grpc.examples.getstuno.GetStuNoServiceGrpc;
import io.grpc.examples.getstuno.StuNoResponse;
import io.grpc.examples.getstuno.StuNoRequest;

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

/**
 * A simple client that requests a greeting from the {@link GetStuNoServer}.
 */
public class GetStuNoClient {
    private static final Logger logger = Logger.getLogger(GetStuNoClient.class.getName());

    private final ManagedChannel channel;
    private final GetStuNoServiceGrpc.GetStuNoServiceBlockingStub blockingStub;

    /** Construct client connecting to GetStuNo server at {@code host:port}. */
    public GetStuNoClient(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. */
    GetStuNoClient(ManagedChannel channel) {
        this.channel = channel;
        blockingStub = GetStuNoServiceGrpc.newBlockingStub(channel);
    }

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

    /** Say hello to server. */
    public void greet(String name) {
        StuNoRequest request = StuNoRequest.newBuilder().setName(name).build();
        StuNoResponse response;
        try {
            response = blockingStub.getMsg(request);
        } catch (StatusRuntimeException e) {
            logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
            return;
        }
        logger.info("Client received: " + response.getNumber());
    }

    /**
     * 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 {
        GetStuNoClient client = new GetStuNoClient("localhost", 50051);
        try {
            /* Access a service running on the local machine on port 50051 */
            String user = "爱丽丝";
            if (args.length > 0) {
                user = args[0]; /* Use the arg as the name to greet if provided */
            }
            client.greet(user);
        } finally {
            client.shutdown();
        }
    }
}

Client端向Server端请求服务

Python

getStuNo.proto

syntax = "proto3";

package getstuno;
 
service GetStuNoService {
 rpc getMsg (StuNoRequest) returns (StuNoResponse){}
}
 
message StuNoRequest {
  string name = 1;
}
 
message StuNoResponse {
  string number = 1;
}

Python与Java的proto文件基本相同,重点在于package必须相同,否则当提供名称、类型相同的service和message时仍然无法沟通。python利用proto生成grpc框架的方法参照:
https://www.cnblogs.com/zongfa/p/12218341.html

server.py

import grpc
import getStuNo_pb2
import getStuNo_pb2_grpc
 
from concurrent import futures
import time
 
_ONE_DAY_IN_SECONDS = 60 * 60 * 24
 
 
class GetStuNoServicer(getStuNo_pb2_grpc.GetStuNoServiceServicer):
 
  def getMsg(self, request, context):
    print("Received name: %s" % request.name)
    if request.name == '爱丽丝':
      number = '1234'
    else:
      number = '0000'
    return getStuNo_pb2.StuNoResponse(number=number)
 
 
def serve():
  server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
  getStuNo_pb2_grpc.add_GetStuNoServiceServicer_to_server(GetStuNoServicer(), server)
  server.add_insecure_port('[::]:50051')
  server.start()
  try:
    while True:
      time.sleep(_ONE_DAY_IN_SECONDS)
  except KeyboardInterrupt:
    server.stop(0)
 
if __name__ == '__main__':
  serve()

client.py

import grpc
 
import getStuNo_pb2
import getStuNo_pb2_grpc
 
def run():
  # NOTE(gRPC Python Team): .close() is possible on a channel and should be
  # used in circumstances in which the with statement does not fit the needs
  # of the code.
  with grpc.insecure_channel('localhost:50051') as channel:
    stub = getStuNo_pb2_grpc.GetStuNoServiceStub(channel)
    response = stub.getMsg(getStuNo_pb2.StuNoRequest(name='爱丽丝'))
  print("Client received: " + response.number)
 
 
if __name__ == '__main__':
  run()

python端的代码较为精简。

此时,保证了java和python端的gRPC提供的gRPC服务名,服务传输的变量名、类型,服务使用的端口相同。先启动java或python任意一个server端,再启动java或python任意一个client端,都可以正确提供gRPC服务。