protoc buf是一种数据压缩格式,因为是二进制格式的,比xml、json序列化快很多。
gRPC由google开发,是一款语言中立、平台中立、开源的远程过程调用系统,基于HTTP2协议标准设计开发
gRPC可以实现微服务,将大的项目拆分为多个小且独立的业务模块,也就是服务,各服务间使用高效的protobuf协议进行RPC调用,gRPC默认使用protocol buffers
支持多种开发语言
一次RPC的完整流程:
客户端(gRPC Sub)调用 A 方法,发起 RPC 调用
对请求信息使用 Protobuf 进行对象序列化压缩(IDL)
服务端(gRPC Server)接收到请求后,解码请求体,进行业务逻辑处理并返回
对响应结果使用 Protobuf 进行对象序列化压缩(IDL)
客户端接受到服务端响应,解码请求体。回调被调用的 A 方法,唤醒正在等待响应(阻塞)的客户端调用并返回响应结果
我们在protobuf协议定义上扩展一个类型定义:Service,这在RPC通讯上有着重要作用。
syntax = "proto3"; // 指定proto版本
// 指定golang包名
option go_package = "pb/proto_demo";
// 定义Hello服务
service Hello {
// 定义SayHello方法
rpc SayHello(HelloRequest) returns (HelloResponse) {}
rpc SayHi(HiRequest) returns (HiResponse) {}
}
// HelloRequest 请求结构
message HelloRequest {
string name = 1;
}
// HelloResponse 响应结构
message HelloResponse {
string message = 1;
}
// HiRequest 请求结构
message HiRequest {
string name = 1;
string school = 2;
int32 age = 3;
int32 grade = 4;
int32 status = 5;
}
// HiResponse 响应结构
message HiResponse {
string message = 1;
int32 status = 2;
}
protoc --go_out=./ --go-grpc_out=./ hello.proto
注意,–go_out告诉protoc编译proto文件生成的目标代码为go代码,–go-grpc_out告诉生成grpc版本的。
这时候会得到2个文件,hello.pb.go和hello_grpc.pb.go,
package main
import (
"fmt"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/grpclog"
"net"
"test/protouse/pb/proto_demo"
)
const (
// gRPC服务地址
Address = "127.0.0.1:9988"
)
type helloService struct {
proto_demo.HelloServer
}
var HelloService = helloService{}
func (h helloService) SayHello(ctx context.Context, in *proto_demo.HelloRequest) (*proto_demo.HelloResponse, error) {
resp := new(proto_demo.HelloResponse)
resp.Message = fmt.Sprintf("yswHello %s.", in.Name)
return resp, nil
}
func (h helloService) SayHi(ctx context.Context, in *proto_demo.HiRequest) (*proto_demo.HiResponse, error) {
resp := new(proto_demo.HiResponse)
resp.Message = fmt.Sprintf("yswHi %s, grade=%d, school=%s, grade=%d, status=%d", in.Name, in.Grade, in.School, in.Grade, in.Status)
return resp, nil
}
func main() {
//test := &proto_demo.Student{
// Name: "yangshengwen",
// Male: true,
// Scores: []int32{23,98, 88},
// Subject: map[string]int32{"age":28, "level":2},
//}
//
//data,err := proto.Marshal(test)
//if err != nil {
// fmt.Println("proto encode err:", err)
// return
//}
//fmt.Printf("data size:%d\n", len(data))
//
//newTest := &proto_demo.Student{}
//err = proto.Unmarshal(data, newTest)
//if err != nil {
// fmt.Println("proto encode err:", err)
// return
//}
//
//fmt.Printf("name:%v, Male:%v, Scores:%v, Subject:%v", newTest.Name, newTest.Male, newTest.Scores, newTest.Subject)
listen, err := net.Listen("tcp", Address)
if err != nil {
grpclog.Fatalf("Failed to listen: %v", err)
}
s := grpc.NewServer()
proto_demo.RegisterHelloServer(s, HelloService)
fmt.Println("Listen on " + Address)
grpclog.Println("Listen on " + Address)
s.Serve(listen)
}
启动server, go run server.go或者编译再运行,就会在对应的端口监听。
package main
import (
"fmt"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/grpclog"
"test/protouse/pb/proto_demo"
)
const (
// gRPC服务地址
Address = "127.0.0.1:9988"
)
func main() {
conn, err := grpc.Dial(Address, grpc.WithInsecure())
if err != nil {
grpclog.Fatalln(err)
}
defer conn.Close()
c := proto_demo.NewHelloClient(conn)
req := &proto_demo.HelloRequest{Name:"grpc"}
res, err := c.SayHello(context.Background(), req)
if err != nil {
grpclog.Fatalln(err)
}
fmt.Println(res.Message)
req2 := &proto_demo.HiRequest{Name:"grpc", Grade:3, Age:10, Status:2, School:"zhuhai"}
res2, err := c.SayHi(context.Background(), req2)
if err != nil {
grpclog.Fatalln(err)
}
fmt.Println(res2.Message)
}
hello_grpc.pb.go里有对应的client实例以及接口,参数用hello.pb.go里对应的结构体。
启动client,go run client.go, 正确得到server进程的回复。