grpc 介绍和 Go 示例
grpc介绍
rpc:远程过程调用。对比本地的函数调用,远程是节点A调用节点B上的服务,比如http等协议的调用。现在流行很多种rpc框架,百度的brpc,谷歌的grpc等。
grpc底层利用的是http/2通信协议,消息采用protocol buffers协议。下面就先介绍下pb。
protocol-buffers介绍
pb谷歌推出的一种信息传输格式,和json、xml的作用一样。 pb是以二进制形式存储传输,压缩数据,传输效率高,人不可读。 一旦定义了要处理的数据的数据结构之后,就可以利用 Protocol buffers 的代码生成工具生成相关的代码。
proto3 语法
新版是proto3,不完全兼容proto2。 在 proto中,所有结构化的数据都被称为 message。
syntax = "proto3";
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
每个字段有一个唯一的编号,这些字段编号用于标识消息二进制格式中的字段。范围 1 到 15 中的字段编号需要一个字节进行编码,包括字段编号和字段类型,范围 16 至 2047 中的字段编号需要两个字节。所以你应该保留数字 1 到 15 作为非常频繁出现的消息元素。
定义service
.proto文件中定义RPC服务接口,protocol buffer 编译器将使用所选语言生成服务接口代码和 stubs
service SearchService {
rpc Search (SearchRequest) returns (SearchResponse);
}
proto示例
proto
syntax = "proto3";
// go的包 当前包名或者包全路径
option go_package = ".;calc";
service CalcService {
rpc Add (RequestData) returns (Response) {}
rpc Sub (RequestData) returns (Response) {}
}
// repeated 可以理解为是个数组
message RequestData {
string req_id = 1;
repeated int32 datas = 2;
}
// 响应
message Response {
string req_id = 1;
int32 result = 2;
}
准备工作
Protocol Buffer Compiler安装
参考:https://grpc.io/docs/protoc-installation/
如Mac
$ brew install protobuf
$ protoc --version # Ensure compiler version is 3+
Go插件下载
$ export GO111MODULE=on # Enable module mode
$ go get google.golang.org/protobuf/cmd/protoc-gen-go \
google.golang.org/grpc/cmd/protoc-gen-go-grpc
$ export PATH="$PATH:$(go env GOPATH)/bin"
生成Go代码
protoc --go_out=plugins=grpc:calc --proto_path=protos calc.proto
plugins=grpc:生成代码的目录 –proto_path proto文件所在目录
执行后自动生成了calc.pb.go文件,打开可以浏览下代码,copy两个接口看下:
type CalcServiceClient interface {
Add(ctx context.Context, in *RequestData, opts ...grpc.CallOption) (*Response, error)
Sub(ctx context.Context, in *RequestData, opts ...grpc.CallOption) (*Response, error)
}
// CalcServiceServer is the server API for CalcService service.
type CalcServiceServer interface {
Add(context.Context, *RequestData) (*Response, error)
Sub(context.Context, *RequestData) (*Response, error)
}
项目组织结构
利用grpc来提供加减法的服务
-
go mod init
go mod init mypro
-
proto生成代码
protoc --go_out=plugins=grpc:calc --proto_path=protos calc.proto
.
├── calc
│ └── calc.pb.go
├── client.go
├── go.mod
├── go.sum
├── protos
│ └── calc.proto
└── server.go
server.go 文件
package main
import (
"context"
"fmt"
"log"
"net"
"google.golang.org/grpc"
"mypro/calc"
)
// server继承自动生成的服务类 参考calc.pb.go内容
type server struct {
calc.UnimplementedCalcServiceServer
}
// 服务端必须实现了相应的接口
func (s *server) Add(ctx context.Context, in *calc.RequestData) (*calc.Response, error) {
fmt.Printf("Add: reqId=%s, %+v\n", in.ReqId, in.Datas)
// 这里缺省检查了
ret := in.Datas[0] + in.Datas[1]
return &calc.Response{ReqId: in.ReqId, Result: ret,}, nil
}
// 服务端必须实现了相应的接口
func (s *server) Sub(ctx context.Context, in *calc.RequestData) (*calc.Response, error) {
fmt.Printf("Sub: reqId=%s, %+v\n", in.ReqId, in.Datas)
// 这里缺省检查了
ret := in.Datas[0] - in.Datas[1]
return &calc.Response{ReqId: in.ReqId, Result: ret,}, nil
}
func main() {
listen, err := net.Listen("tcp", ":65500")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
// grpc.NewServer
s := grpc.NewServer()
// 注册 参考calc.pb.go文件
calc.RegisterCalcServiceServer(s, &server{})
if err := s.Serve(listen); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
client.go 文件
package main
import (
"context"
"fmt"
"log"
"strconv"
"time"
"google.golang.org/grpc"
"mypro/calc"
)
func main() {
// Set up a connection to the server.
conn, err := grpc.Dial("localhost:65500", grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
log.Fatalf("connect failed: %v", err)
}
defer conn.Close()
// 参考calc.pb.go
c := calc.NewCalcServiceClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
data1, data2 := int32(111), int32(222)
// 调用Add方法 类似本地调用
resp, err := c.Add(ctx, &calc.RequestData{
ReqId: strconv.FormatInt(time.Now().UnixNano(), 10),
Datas: []int32{data1, data2},
})
if err != nil {
log.Fatalf("error: %v", err)
}
fmt.Printf("Add reqId: %s, result: %d\n", resp.ReqId, resp.Result)
// 调用Sub方法
resp1, err := c.Sub(ctx, &calc.RequestData{
ReqId: strconv.FormatInt(time.Now().UnixNano(), 10),
Datas: []int32{data2, data1},
})
if err != nil {
log.Fatalf("error: %v", err)
}
fmt.Printf("Sub reqId: %s, result: %d\n", resp1.ReqId, resp1.Result)
}
通过写一个grpc服务,我们能大概了解它的工作流程。想深入学习,可以参考源码或者一些文档。
参考
- https://developers.google.com/protocol-buffers/docs/proto3
- https://halfrost.com/protobuf_encode/
- https://grpc.io/docs/what-is-grpc/introduction/
- https://grpc.io/docs/protoc-installation/
- grpc Go语言示例