Golang Python通过gRPC跨语言调用DEMO

  • Post author:
  • Post category:golang



1、首先Python端和Golang端都要安装gRPC包和相应的源码生成工具

python(适用于3.5以上版本):

python -m pip install grpcio
python -m pip install grpcio-tools

golang:

go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2

同时下载protocol buffer的编译工具:



Releases · protocolbuffers/protobuf · GitHub



https://github.com/protocolbuffers/protobuf/releases


我这里下载了个最新的但是会报错,所以我下载了个20年的老版本的protoc.exe,将其解压后,配置到PATH环境变量中,之后会用到protoc指令来生成存根(stub)代码


2、建立python、golang项目

项目路径如下:



3、编写myproto.proto文件

这个文件就好似一个协议一样,约束双方的请求和响应内容,因此一定要确保这里在python项目中的proto文件和golang项目中的proto文件内容要完全一样,否则后面会调用失败。

【注意:.proto文件中的package必须在两个文件中保持一致,因为后面它会根据这个package来生成源码不一致的话会有问题的,所以这里的最佳实践是要么就不用package,如果要用package要保证两语言端的package写的是同一个,这是一个小坑但在这里卡了比较久,所以强调一下】

myproto.proto:

syntax = "proto3";

option go_package = "./;proto";

package proto;

service Calculator {
  rpc Add (HelloRequest) returns (HelloReply) {}
}

message HelloRequest {
  int32 a = 1;
  int32 b = 2;
}

message HelloReply {
  int32 res = 1;
}


4、Python端、Golang端分别通过proto文件生成存根源码

对于golang端,cd到/grpc_final_test路径上,执行

protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative ./proto/myproto.proto

对于python端,同样cd到python端的/grpc_final_test路径,执行

python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. .\proto\myproto.proto

如果没有其他报错的话,可以看到在./proto文件夹下双方都生成了两个源码文件,这就是grpc-tool为我们生成的一个server和client的存根源码;有一点需要注意,python端生成的myproto_pb2_grpc.py文件中的import可以看到有报错,这个是grpc官方没解决的一个小问题,这里我们只需要将from proto 改为 from . 即可。


5、编写Golang端的Server和Client代码

Client

package main

import (
	pb "awesomeProject/grpc_final_test/proto"
	"context"
	"fmt"

	"google.golang.org/grpc"
)

func main() {
	conn, err := grpc.Dial("127.0.0.1:50051", grpc.WithInsecure())
	if err != nil {
		panic(err)
	}
	defer conn.Close()
	c := pb.NewCalculatorClient(conn)
	r, err := c.Add(context.Background(), &pb.HelloRequest{A: 5, B: 6})
	if err != nil {
		panic(err)
	}
	fmt.Println(r.Res, r.Message)
}

Server

package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"net"

	pb "awesomeProject/grpc_final_test/proto"

	"google.golang.org/grpc"
)

var (
	port = flag.Int("port", 50051, "The server port")
)

type server struct {
	pb.UnimplementedCalculatorServer
}

func (s *server) Add(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	log.Printf("Received: a=%v b=%v", in.GetA(), in.GetB())
	return &pb.HelloReply{Res: in.GetA() + in.GetB(), Message: "来自Golang服务端的响应"}, nil
}

func main() {
	flag.Parse()
	lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	s := grpc.NewServer()
	pb.RegisterCalculatorServer(s, &server{})
	log.Printf("server listening at %v", lis.Addr())
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}


6、编写Python端的Server和Client代码

Client

from grpc_final_test.proto import myproto_pb2, myproto_pb2_grpc
import grpc

if __name__ == '__main__':
    with grpc.insecure_channel("localhost:50051") as channel:
        stub = myproto_pb2_grpc.CalculatorStub(channel)
        rsp: myproto_pb2.HelloReply = stub.Add(myproto_pb2.HelloRequest(a=1, b=2))
        print(rsp.res, rsp.message)

Server

import grpc
from concurrent import futures
from grpc_final_test.proto import myproto_pb2, myproto_pb2_grpc

class Computer(myproto_pb2_grpc.CalculatorServicer):
    def Add(self, request, context):
        return myproto_pb2.HelloReply(res=request.a + request.b, message="来自Python服务端的响应")


if __name__ == '__main__':
    # 1 实例化Server
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    # 2 注册逻辑到server中
    myproto_pb2_grpc.add_CalculatorServicer_to_server(Computer(), server)
    # 3 启动server
    server.add_insecure_port("[::]:50051")
    server.start()
    server.wait_for_termination()

7、双向调用测试

启动Golang的Server,在Python端调用

启动Python的Server,在Golang端调用;

完成python golang通过gRPC框架双向跨语言调用的DEMO



版权声明:本文为qq_29941979原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。