grpc 在python与golang中的使用
一 Proto中
首先定义data.proto文件
syntax = "proto3";
package example;
service FormatData { //定义服务,用在rpc传输中
rpc DoFormat(actionrequest) returns (actionresponse){} //要使用的函数
}
message actionrequest { // 定义请求结构
required string text = 1;
}
message actionresponse{ // 定义返回结构
required string text=1;
}
其中 DoFormat ,actionrequest与actionresponse 可定义多个,数据类型同样可定义
message actionrequest { // 定义请求结构
string text = 1; // tring 代表类型 text 代表字段
string name = 2;
repeated string phones = 3; // repeated 这种字段可以重复任意多次(包括0次)。重复的值的顺序会被保留。表示该值可以重复,相当于Java中的List,python 中的list ,golang 中的slice
}
数据类型除了string外还有
.proto类型 | Java 类型 | C++类型 | 备注 |
---|---|---|---|
double | double | double | |
float | float | float | |
int32 | int | int32 | 使用可变长编码方式。编码负数时不够高效——如果你的字段可能含有负数,那么请使用sint32。 |
int64 | long | int64 | 使用可变长编码方式。编码负数时不够高效——如果你的字段可能含有负数,那么请使用sint64。 |
uint32 | int[1] | uint32 | Uses variable-length encoding. |
uint64 | long[1] | uint64 | Uses variable-length encoding. |
sint32 | int | int32 | 使用可变长编码方式。有符号的整型值。编码时比通常的int32高效。 |
sint64 | long | int64 | 使用可变长编码方式。有符号的整型值。编码时比通常的int64高效。 |
fixed32 | int[1] | uint32 | 总是4个字节。如果数值总是比总是比228大的话,这个类型会比uint32高效。 |
fixed64 | long[1] | uint64 | 总是8个字节。如果数值总是比总是比256大的话,这个类型会比uint64高效。 |
sfixed32 | int | int32 | 总是4个字节。 |
sfixed64 | long | int64 | 总是8个字节。 |
bool | boolean | bool | |
string | String | string | 一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本。 |
bytes | ByteString | string | 可能包含任意顺序的字节数据。 |
1 枚举
当需要定义一个消息类型的时候,可能想为一个字段指定某“预定义值序列”中的一个值。例如,假设要为每一个SearchRequest消息添加一个 corpus字段,而corpus的值可能是UNIVERSAL,WEB,IMAGES,LOCAL,NEWS,PRODUCTS或VIDEO中的一个。 其实可以很容易地实现这一点:通过向消息定义中添加一个枚举(enum)就可以了。一个enum类型的字段只能用指定的常量集中的一个值作为其值(如果尝 试指定不同的值,解析器就会把它当作一个未知的字段来对待)。在下面的例子中,在消息格式中添加了一个叫做Corpus的枚举类型——它含有所有可能的值 ——以及一个类型为Corpus的字段:
message SearchRequest {
required string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
enum Corpus {
UNIVERSAL = 0;
WEB = 1;
IMAGES = 2;
LOCAL = 3;
NEWS = 4;
PRODUCTS = 5;
VIDEO = 6;
}
Corpus corpus = 4 ;
}
2 嵌套类型
message SearchResponse {
message Result {
string url = 1;
string title = 2;
string snippets = 3;
}
repeated Result result = 1;
}
二 Python 中使用
安装:
-
gRPC 的安装:
$ pip install grpcio
-
安装 ProtoBuf 相关的 python 依赖库:
$ pip install protobuf
-
安装 python grpc 的 protobuf 编译工具:
$ pip install grpcio-tools
目录结构
.
├── client
│ ├── __init__.py
│ └── main.py
├── example
│ ├── data_pb2_grpc.py
│ ├── data_pb2.py
│ ├── data.pb.go
│ ├── data.proto
│ ├── __init__.py
│ └── __pycache__
│ ├── data_pb2.cpython-36.pyc
│ └── data_pb2_grpc.cpython-36.pyc
└── service
├── __init__.py
└── main.py
首先定义定义./example/data.proto
syntax = "proto3";
package example;
service FormatData { //定义服务,用在rpc传输中
rpc DoFormat(actionrequest) returns (actionresponse){}
}
message actionrequest {
string text = 1;
enum Corpus {
UNIVERSAL = 0;
WEB = 1;
IMAGES = 2;
LOCAL = 3;
NEWS = 4;
PRODUCTS = 5;
VIDEO = 6;
}
Corpus corpus = 4;
}
message actionresponse{
string text=1;
int32 age = 2;
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
repeated Result result = 3;
}
编译
在 ./example 文件夹下
python -m grpc_tools.protoc -I . --python_out=. --grpc_python_out=. data.proto
执行后生成了
│ ├── data_pb2_grpc.py
│ ├── data_pb2.py
两个文件
编写服务端 ./service/main.py
#! /usr/bin/env python
# -*- coding: utf-8 -*-
import grpc
import time
from concurrent import futures
import os
import sys
current_fonder_path = os.path.split(os.path.realpath(__file__))[0]
print (current_fonder_path)
protocal_path = os.path.join(current_fonder_path,"..","example")
print (protocal_path)
sys.path.append(protocal_path)
import data_pb2,data_pb2_grpc
_ONE_DAY_IN_SECONDS = 60 * 60 * 24
_HOST = 'localhost'
_PORT = '8082'
# 实现一个派生类,重写rpc中的接口函数.自动生成的grpc文件中比proto中的服务名称多了一个Servicer
class FormatData(data_pb2_grpc.FormatDataServicer):
# 重写接口函数.输入和输出都是proto中定义的Data类型
def DoFormat(self, request, context):
str = request.text
print(request.corpus)
return data_pb2.actionresponse(text=str.upper(),age=12,result=[{"url":"12","title":"12","snippets":["12","12"]},{"url":"12","title":"12","snippets":["12","12"]}]) # 返回一个类实例
def serve():
# 定义服务器并设置最大连接数,corcurrent.futures是一个并发库,类似于线程池的概念
grpcServer = grpc.server(futures.ThreadPoolExecutor(max_workers=4)) # 创建一个服务器
data_pb2_grpc.add_FormatDataServicer_to_server(FormatData(), grpcServer) # 在服务器中添加派生的接口服务(自己实现了处理函数)
grpcServer.add_insecure_port(_HOST + ':' + _PORT) # 添加监听端口
grpcServer.start() # 启动服务器
try:
while True:
time.sleep(_ONE_DAY_IN_SECONDS)
except KeyboardInterrupt:
grpcServer.stop(0) # 关闭服务器
if __name__ == '__main__':
serve()
编写客户端代码./client/main.py
#! /usr/bin/env python
# -*- coding: utf-8 -*-
import grpc
import os
import sys
from grpc._channel import _Rendezvous
current_fonder_path = os.path.split(os.path.realpath(__file__))[0]
print (current_fonder_path)
protocal_path = os.path.join(current_fonder_path,"..","example")
print (protocal_path)
sys.path.append(protocal_path)
import data_pb2, data_pb2_grpc
_HOST = 'localhost'
_PORT = '8082'
def run():
conn = grpc.insecure_channel(_HOST + ':' + _PORT) # 监听频道
client = data_pb2_grpc.FormatDataStub(channel=conn) # 客户端使用Stub类发送请求,参数为频道,为了绑定链接
response = client.DoFormat(data_pb2.actionrequest(text='hello,world!',corpus="NEWS")) # 返回的结果就是proto中定义的类
for i in response.result:
print(i)
if __name__ == '__main__':
try:
run()
except _Rendezvous as e:
print("connect error")
三 golang 中使用
由于是使用golang调用python代码,所以golang中未实现service
安装:
首先安装protoc
安装依赖
sudo yum -y install autoconf automake libtool curl make g++ unzip
或
sudo apt-get install -y install autoconf automake libtool curl make g++ unzip
在 https://github.com/protocolbuffers/protobuf/releases 下载最新tar.gz安装包(protobuf-all-3.8.0.tar.gz
)
$ tar xvzf protobuf-all-3.8.0.tar.gz
$ cd protobuf-3.8.0
$ ./configure --prefix=/usr/local/protobuf
$ make (需要很长时间)
$ sudo make check
$ sudo make install
$ sudo ln -s /usr/local/protoc /bin//protoc
然后安装go插件
go get -u github.com/golang/protobuf/protoc-gen-go
然后在在 ./example 文件夹下
$ protoc --go_out=plugins=grpc:. ./data.proto
生成文件
./example/data.pb.go
将data.pb.go文件拷贝到要使用的地方
目录结构
.
└── demo1
├── main.go
└── rpc
└── data.pb.go
然后安装gorpc库
官网的安装命令:
go get -u google.golang.org/grpc
结果不能下载
# 如果已经安装了proto和protoc-gen-go的话就不用安装了
go get -u github.com/golang/protobuf/{proto,protoc-gen-go}
# 下载grpc-go
git clone https://github.com/grpc/grpc-go.git $GOPATH/src/google.golang.org/grpc
# 下载golang/net
git clone https://github.com/golang/net.git $GOPATH/src/golang.org/x/net
# 下载golang/text
git clone https://github.com/golang/text.git $GOPATH/src/golang.org/x/text
# 下载go-genproto
git clone https://github.com/google/go-genproto.git $GOPATH/src/google.golang.org/genproto
./demo1/main.go文件
package main
//client.go
import (
"fmt"
"golang.org/x/net/context"
"google.golang.org/grpc"
pd "grpcdemo/demo1/rpc"
"log"
"reflect"
)
const (
address = "localhost:8082"
)
func main() {
conn, err := grpc.Dial(address, grpc.WithInsecure())
if err != nil {
log.Fatal("did not connect: %v", err)
}
defer conn.Close()
c := pd.NewFormatDataClient(conn)
r, err := c.DoFormat(context.Background(), &pd.Actionrequest{Text:"test",Corpus:pd.Actionrequest_NEWS})
if err != nil {
log.Fatal("could not greet: %v", err)
}
fmt.Println(r.Age)
fmt.Println(reflect.TypeOf(r.Result))
for k,v:= range r.Result{
fmt.Println(k,v)
fmt.Println(v.Snippets)
}
}