gRPC是由 google 主导开发的RPC框架,使用HTTP/2协议并用ProtoBuf作为序列化工具。
例1.该示例源自gRPC-go的examples的helloworld
proto文件
syntax = “proto3”;
option objc_class_prefix = “HLW”;
package helloworld;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
Message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
service定义了一个 server 。其中的接口可以是四种类型
a.rpc Get Feature (Point) returns (Feature) {} 类似普通的函数调用,客户端发送请求Point到服务器,服务器返回相应Feature.
b.rpc ListFeatures(Rectangle) returns (stream Feature) {}客户端发起一次请求,服务器端返回一个流式数据,比如一个数组中的逐个元素
c.rpc RecordRoute(stream Point) returns (RouteSummary) {}
客户端发起的请求是一个流式的数据,比如数组中的逐个元素,服务器返回一个相应
d.rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
客户端发起的请求是一个流式数据,比如数组中的逐个元素,二服务器返回的也是一个类似的数据结构
2.使用protoc命令生成相关文件:
protoc –go_out=plugins=grpc:. helloworld.proto
helloworld.pb.go helloworld.proto
生成对应的pb.go文件。
3.服务端程序
package main
import (
“log”
” net ”
pb “your_path_to_gen_pb_dir/helloworld”
“golang.org/x/net/context”
“google.golang.org/grpc”
)
const (
port = “:50051”
)
type server struct{}
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
return &pb.HelloReply{Message: “Hello ” + in.Name}, nil
}
func main() {
lis, err := net.Listen(“tcp”, port)
if err != nil {
log.Fatalf(“failed to listen: %v”, err)
}
s := grpc.NewServer() //调用grpc.NewServer() 创建一个server s
pb.RegisterGreeterServer(s, &server{}) //注册这个server s到结构server上面
s.Serve(lis)
}
4.客户端程序:
package main
import (
“log”
“os”
pb “your_path_to_gen_pb_dir/helloworld”
“golang.org/x/net/context”
“google.golang.org/grpc”
)
const (
address = “localhost:50051”
defaultName = “world”
)
func main() {
conn, err := grpc.Dial(address, grpc.WithInsecure())
if err != nil {
log.Fatalf(“did not connect: %v”, err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn)
name := defaultName
if len(os. Args ) > 1 {
name = os.Args[1]
}
r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: name})
if err != nil {
log.Fatalf(“could not greet: %v”, err)
}
log.Printf(“Greeting: %s”, r.Message)
}
先运行服务器,在运行客户端,可以看到。
./greeter_server &
./greeter_client
2018/03/10 21:42:19 Greeting: Hello world
例2.ProtoBuf作为序列化
1、下载protobuf的编译器protoc
下载:protoc-3.3.0-linux-x86_64.zip解压,把bin目录下的protoc复制到GOPATH/bin下,GOPATH/bin加入环境变量。
获取protobuf的编译器插件protoc-gen-go,进入GOPATH目录
go get -u github.com/golang/protobuf/protoc-gen-go,如果成功,会在GOPATH/bin下生成protoc-gen-go.exe文件
2.创建一个test.proto文件
//指定版本
syntax = “proto3”;
package test;
//手机类型
//枚举类型第一个字段必须为0
enum PhoneType {
HOME = 0;
WORK = 1;
}
//手机
message Phone {
PhoneType type = 1;
string number = 2;
}
//人
message Person {
//后面的数字表示标识号
int32 id = 1;
string name = 2;
//repeated表示可重复
//可以有多个手机
repeated Phone phones = 3;
}
//联系簿
message ContactBook {
repeated Person persons = 1;
}
3.运行如下命令
protoc –go_out=. *.proto
会生成一个test.pb.go的文件
4.在go语言中使用protobuf
package main;
import (
“github.com/golang/protobuf/proto”
“protobuf/test”
“io/ioutil”
“os”
“fmt”
)
func write() {
p1 := &test.Person{
Id: 1,
Name: “小张”,
Phones: []*test.Phone{
{test.PhoneType_HOME, “111111111”},
{test.PhoneType_WORK, “222222222”},
},
};
p2 := &test.Person{
Id: 2,
Name: “小王”,
Phones: []*test.Phone{
{test.PhoneType_HOME, “333333333”},
{test.PhoneType_WORK, “444444444”},
},
};
//创建地址簿
book := &test.ContactBook{};
book.Persons = append (book.Persons, p1);
book.Persons = append(book.Persons, p2);
//编码数据
data, _ := proto.Marshal(book);
//把数据写入文件
ioutil.WriteFile(“./test.txt”, data, os.ModePerm);
}
func read() {
//读取文件数据
data, _ := ioutil.ReadFile(“./test.txt”);
book := &test.ContactBook{};
//解码数据
proto.Unmarshal(data, book);
for _, v := range book.Persons {
fmt.Println(v.Id, v.Name);
for _, vv := range v.Phones {
fmt.Println(vv.Type, vv.Number);
}
}
}
func main() {
write();
read();
}
结果:
1 小张
HOME 1111111111
WORK 2222222222
2 小王
HOME 3333333333
WORK 4444444444