python(适用于3.5以上版本):
python -m pip install grpcio
python -m pip install grpcio-tools
golang:
go install google.golang.org/protobuf/cmd/[email protected]
go install google.golang.org/grpc/cmd/[email protected]
同时下载protocol buffer的编译工具:Releases · protocolbuffers/protobuf · GitHubhttps://github.com/protocolbuffers/protobuf/releases
我这里下载了个最新的但是会报错,所以我下载了个20年的老版本的protoc.exe,将其解压后,配置到PATH环境变量中,之后会用到protoc指令来生成存根(stub)代码
项目路径如下:
这个文件就好似一个协议一样,约束双方的请求和响应内容,因此一定要确保这里在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;
}
对于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 . 即可。
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)
}
}
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()
启动Golang的Server,在Python端调用
启动Python的Server,在Golang端调用;
完成python golang通过gRPC框架双向跨语言调用的DEMO