gRPC是由google开发,是一款跨平台,跨语言,开源的远程过程调用(RPC)系统。Docker v1.12.3就是通过gRPC调用containerd的。所以本次笔记介绍基于官方例子改写的一个例子,分别用Go和Python实现。
环境搭建 略(由于google.golang.org被墙,所以我是通过containerd源码包中的src来解决依赖的,不方便)。
proto gRPC的序列化由protobuf完成。我们先定义helloworld.proto文件,里面定义了Greeter service, HelloRequest message和HelloReply。其中HelloRequest message定义了普通变量,enum变量,数组变量及oneof和any变量。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
syntax = "proto3";
option java_package = "io.grpc.examples";
package helloworld;
import "google/protobuf/any.proto";
// The greeter service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
enum Gender {
FEMALE = 0;
MALE = 1;
}
Gender gender = 2;
repeated string hobbies = 3;
oneof office {
string school = 4;
string company = 5;
}
map<string, string> phone = 6;
google.protobuf.Any other = 7;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
使用protoc --go_out=plugins=grpc:. helloworld.proto
可生成helloworld.pb.go文件,即Go语言需要调用的文件。 使用python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. helloworld.proto
可生成helloworld_pb2_grpc.py和helloworld_pb2.py文件,即Python语言需要调用的文件。
Go server.go1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package main
import (
"log"
"fmt"
"net"
"golang.org/x/net/context"
"google.golang.org/grpc"
pb "helloworld/helloworld"
)
const (
port = ":50051"
)
type server struct {}
func (s *server) SayHello (ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
name := fmt.Sprintf("%q" , in.GetName())
hobbies := fmt.Sprintf("%q" , in.GetHobbies())
gender := fmt.Sprintf("%q" , in.GetGender())
office := fmt.Sprintf("%q" , in.GetOffice())
phone := fmt.Sprintf("%q" , in.GetPhone())
other := fmt.Sprintf("%q" , in.GetOther().GetValue())
return &pb.HelloReply{Message: "Name: " + name + " , Gender: " + gender + " , Hobbies: " + hobbies + " , Office: " + office + ", Phone: " + phone + " , Other: " + other}, nil
}
func main () {
lis, err := net.Listen("tcp" , port)
if err != nil {
log.Fatal("failed to listen: %v" , err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
s.Serve(lis)
}
client.go1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package main
import (
"log"
"os"
"golang.org/x/net/context"
"google.golang.org/grpc"
"github.com/golang/protobuf/ptypes/any"
pb "helloworld/helloworld"
)
const (
address = "127.0.0.1:50051"
defaultName = "Jack"
)
func main () {
conn, err := grpc.Dial(address, grpc.WithInsecure())
if err != nil {
log.Fatal("did not connect: %v" , err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn)
name := defaultName
if len (os.Args) >1 {
name = os.Args[1 ]
}
gender := pb.HelloRequest_MALE
hobbies := []string {"swimming" , "running" , "football" }
office := &pb.HelloRequest_School{"Fudan University" }
phone := map [string ]string {"telephphone" : "13900010001" , "phone" : "123456" }
other := &any.Any{Value: []byte ("This is my greet!" )}
request := pb.HelloRequest{Name: name, Gender: gender, Hobbies: hobbies, Office: office, Phone: phone, Other: other}
r, err := c.SayHello(context.Background(), &request)
if err != nil {
log.Fatal("could not greet: %v" , err)
}
log.Printf("Greeting from: %s" , r.Message)
}
运行go run server.go
和go run client.go
,在client端,可得到:1
2017/11/11 15:57:23 Greeting from: Name: "Jack" , Gender: "MALE" , Hobbies: ["swimming" "running" "football"] , Office: &{"Fudan University"}, Phone: map["phone":"123456" "telephphone":"13900010001"] , Other: "This is my greet!"
Python server.py:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
"""The Python implementation of the GRPC helloworld.Greeter server."""
from concurrent import futures
import time
import string
import grpc
from helloworld import helloworld_pb2
from helloworld import helloworld_pb2_grpc
_ONE_DAY_IN_SECONDS = 60 * 60 * 24
class Greeter (helloworld_pb2_grpc.GreeterServicer) :
def SayHello (self, request, context) :
name = request.name
hobbies = request.hobbies
gender = ""
if request.gender == helloworld_pb2.HelloRequest.MALE:
gender = "MALE"
else :
gender = "FEMALE"
office = request.school
phone = request.phone
other = request.other.value
message = "Name: " + name + " , Gender: " + gender + " , Hobbies: " + str(hobbies) + " , Office: " + office + ", Phone: " + str(phone.items()) + " , Other: " + other
return helloworld_pb2.HelloReply(message=message)
def serve () :
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10 ))
helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), 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.py1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from __future__ import print_function
import grpc
from helloworld import helloworld_pb2
from helloworld import helloworld_pb2_grpc
from google.protobuf import any_pb2
def run () :
channel = grpc.insecure_channel('127.0.0.1:50051' )
stub = helloworld_pb2_grpc.GreeterStub(channel)
name = "Jack"
gender = "MALE"
hobbies = ["swimming" , "running" , "football" ]
school = "Fudan"
phone = {"telephphone" : "13900010001" , "phone" : "123456" }
other = any_pb2.Any(value="This is my greet!" )
request = helloworld_pb2.HelloRequest(name=name, gender=gender, hobbies=hobbies, school=school, phone=phone, other=other)
response = stub.SayHello(request)
print(response.message)
if __name__ == '__main__' :
run()
运行python server.go
和python client.go
,在client端,可得到:1
Name: Jack , Gender: MALE , Hobbies: [u'swimming', u'running', u'football'] , Office: Fudan, Phone: [(u'phone', u'123456'), (u'telephphone', u'13900010001')] , Other: This is my greet!
总结 protobuf的语法网上有很多资源。需要着重说明的是,上面的server.py和client.go也可以得到正确的结果,这充分说明了gRPC是跨语言的,很神奇。