gRPC
grpc的优势:
以下将通过例子实现go和python之间的gRPC,实现简单地传输一个字符串
python:3.5.9 golang:1.13.4 protobuf:3.11.3proto定义规则
ProtoBuf3数据结构定义
message 消息结构类型名{
字段修饰符 数据类型 字段名 = 字段编码值;
}
singular 默认值,标识成员只有0个或者1个
repeated 可以重复0次或多次
用于通信双方识别对方的字段,通过同一份协议的结构体每个编码值对应的数据字段是一样的。取值范围是 1 ~ 2^32
举例
message String{
string query = 1;
int32 page_number = 2; // Which page number do we want
int32 result_per_page = 3; // Number of results to return per page
enum Corpus {
UNIVERSAL = 0;
WEB = 1;
IMAGES = 2;
LOCAL = 3;
NEWS = 4;
PRODUCTS = 5;
VIDEO = 6;
}
Corpus corpus = 4;
repeated int 32 num = 7;
}
定义服务
客户端使用 Stub 发送请求到服务器并等待响应返回,就像平常的函数调用一样,这是一个阻塞型的调用
service 服务类型名 {
rpc 服务名 (消息类型) returns (返回值消息类型);
}
客户端发送请求到服务器,拿到一个流去读取返回的消息序列。客户端读取返回的流,直到里面没有任何消息。
service 服务类型名 {
rpc 服务名 (消息类型) returns (stream 返回值消息类型);
}
客户端写入一个消息序列并将其发送到服务器,同样也是使用流。一旦客户端完成写入消息,它等待服务器完成读取返回它的响应。
service 服务类型名 {
rpc 服务名 (stream 消息类型) returns (返回值消息类型);
}
service 服务类型名 {
rpc 服务名 (stream 消息类型) returns (stream 返回值消息类型);
}
引用其他proto
// my.proto
import "first.proto";
golang和python编译proto
python编译proto
首先安装grpcio-tools和grpcio
pip install grpcio -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com
pip install grpcio-tools -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com
然后python编译proto
python -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I. rpc.proto
会生成两个文件
rpc_pb2.py 用来和 protobuf 数据进行交互 rpc_pb2_grpc 用来和 grpc 进行交互golang编译proto
首先要安装grpc,由于环境的问题许多go的库难以获取,我将库都转移到了码云上,这样下载十分方便
git clone https://gitee.com/daba0007/grpc-go.git $GOPATH/src/google.golang.org/grpc
git clone https://gitee.com/daba0007/net.git $GOPATH/src/golang.org/x/net
git clone https://gitee.com/daba0007/text.git $GOPATH/src/golang.org/x/text
git clone https://gitee.com/daba0007/sys.git $GOPATH/src/golang.org/x/sys
go get -u github.com/golang/protobuf/{proto,protoc-gen-go}
git clone https://gitee.com/daba0007/go-genproto.git $GOPATH/src/google.golang.org/genproto
cd $GOPATH/src/
go install google.golang.org/grpc
cp $GOPATH/bin/protoc-gen-go /usr/local/bin
然后编译proto
protoc --go_out=plugins=grpc:. rpc.proto
这样会生成一个rpc.pb.go的文件,用来和 protobuf 数据进行交互
Demo
定义proto
通过protobuf定义接口HelloService和数据类型String
// rpc.proto
syntax = "proto3";
package go_protoc;
// 定义数据类型String
message String {
string value = 1;
}
// 定义接口HelloService
service HelloService {
rpc Hello (String) returns (String);
}
Go做服务端,python做客户端
// 编译出来的proto文件位于$GOPATH/src/go_protoc
package main
import (
"context"
"go_protoc"
"google.golang.org/grpc"
"log"
"net"
)
// 定义一个对象来处理接收到的protobuf
type HelloServiceImpl struct{}
func (p *HelloServiceImpl) Hello(ctx context.Context, args *go_protoc.String,) (*go_protoc.String, error) {
// 通过编译出来的rpc.pb.go解析String类型数据
reply := &go_protoc.String{Value: "hello:" + args.GetValue()}
return reply, nil
}
func main() {
// 定义一个grpc
grpcServer := grpc.NewServer()
// 通过编译出来的rpc.pb.go的HelloService接口定义一个服务RegisterHelloServiceServer
go_protoc.RegisterHelloServiceServer(grpcServer, new(HelloServiceImpl))
// 定义监听端口1234
lis, err := net.Listen("tcp", "192.168.1.146:1234")
if err != nil {
log.Fatal(err)
}
// 开启监听
grpcServer.Serve(lis)
}
import rpc_pb2
import grpc
import rpc_pb2_grpc
# 连接 rpc 服务器
channel = grpc.insecure_channel('192.168.1.146:1234')
# 调用rpc服务,通过编译出来的rpc_pb2_grpc的HelloService接口定义HelloServiceStub接口,接收来自channel的数据
stub = rpc_pb2_grpc.HelloServiceStub(channel)
# 通过接口的rpc获取String类型数据,并获取值
response = stub.Hello(rpc_pb2.String(value='test'))
print("Greeter client received: " + response.value)
python做服务端,go做客户端
from concurrent import futures
import time
import rpc_pb2
import grpc
import rpc_pb2_grpc
class Hello(rpc_pb2_grpc.HelloServiceServicer):
# 实现 proto 文件中定义的 rpc 调用
def Hello(self, request, context):
return rpc_pb2.String(value = 'hello {msg}'.format(msg = request.value))
# 定义开启4个线程处理接收到的请求
server = grpc.server(futures.ThreadPoolExecutor(max_workers=4))
# 将编译出来的rpc_pb2_grpc的add_HelloServiceServicer_to_server函数添加到server中
rpc_pb2_grpc.add_HelloServiceServicer_to_server(Hello(), server)
# 定义服务端端口1234
server.add_insecure_port('192.168.1.146:1234')
server.start()
# 长期监听
try:
while True:
time.sleep(60 * 60 * 24)
except KeyboardInterrupt:
server.stop(0)
package main
import (
"fmt"
"go_protoc"
"google.golang.org/grpc"
"log"
"golang.org/x/net/context"
)
func main() {
// 连接服务端接口
conn, err := grpc.Dial("192.168.1.146:1234", grpc.WithInsecure())
if err != nil {
log.Fatal(err)
}
defer conn.Close()
// 通过编译rpc.pb.go得到NewHelloServiceClient函数来处理连接
client := go_protoc.NewHelloServiceClient(conn)
// 通过编译rpc.pb.go得到的Hello服务来发送数据类型为String的数据
reply, err := client.Hello(context.Background(), &go_protoc.String{Value: "test"})
if err != nil {
log.Fatal(err)
}
fmt.Println(reply.GetValue())
}