Go的Protobuf与GRPC
Protobuf与GRPC
Protobuf
https://github.com/google/protobuf/releases
解压后,把bin路径加入环境变量,比如D:\Program Files\protoc-3.19.2-win64\bin
然后安装protoc的Go插件:
go get -u github.com/golang/protobuf/protoc-gen-go
编译插件protoc-gen-go
默认会安装到$GOBIN
,是$GOPATH/bin
。如果之前没把GOPATH/bin加入环境变量的话,还得GOPATH/bin加入环境变量。GOPATH路径通过go env查看
还有命令安装protobuf包:
go get google.golang.org/protobuf
在工程中建个proto文件夹,然后新建student.proto文件
syntax = "proto3"; // 版本声明,使用Protocol Buffers v3版本
//option go_package = "path;name";
//path 表示生成的go文件的存放地址,会自动生成目录的。
//name 表示生成的go文件所属的包名
option go_package="./;proto";
enum ClassName{ //枚举
class1=0; //标号 必须从 0开始
class2=1;
class3=2;
}
message Student{ //消息,对应于Go的结构体
string name=1; //1:标号,唯一 即可(相当于数据库中的Id,不一定要从1 ,2的顺序依次排列。)
int32 age=2; //必须指定整型的范围,如int32,int64
string address=3;
ClassName cn=4;
}
message Students{
repeated Student person=1; // repeated 修饰,相当于Go中切片
string school=2;
}
然后在proto文件夹下执行:
protoc -I . student.proto --go_out=.
#-I . 指定文件路径 .表示当前路径下
#student.proto就是文件
#--go_out 指定生成go的文件
# =. 在当前路径下生成
此时在当前目录下会生成一个student.pb.go
文件,我们的Go语言代码里就是使用这个文件。
工程结构
grpctest
├── go.mod
├── go.sum
├── main.go
└── proto
├── student.pb.go
└── student.proto
测试代码,main:
package main
import (
"fmt"
"google.golang.org/protobuf/proto"
mypro "grpctest/proto"
)
func main() {
s1 := &mypro.Student{} //第一个学生信息
s1.Name = "jz01"
s1.Age = 23
s1.Address = "cq"
s1.Cn = mypro.ClassName_class2 //枚举类型赋值
ss := &mypro.Students{}
ss.Person = append(ss.Person, s1) //将第一个学生信息添加到Students对应的切片中
s2 := &mypro.Student{} //第二个学生信息
s2.Name = "jz02"
s2.Age = 25
s2.Address = "cd"
s2.Cn = mypro.ClassName_class3
ss.Person = append(ss.Person, s2) //将第二个学生信息添加到Students对应的切片中
ss.School = "cqu"
fmt.Println("Students信息为:", ss)
// Marshal takes a protocol buffer message
// and encodes it into the wire format, returning the data.
buffer, _ := proto.Marshal(ss)
fmt.Println("序列化之后的信息为:", buffer)
// Use UnmarshalMerge to preserve and append to existing data.
data := &mypro.Students{}
proto.Unmarshal(buffer, data)
fmt.Println("反序列化之后的信息为:", data)
}
GRPC
安装gRPC
go get -u google.golang.org/grpc
编写proto文件,helloworld.proto
grpc的proto文件,比原来的要写多service和回复message
syntax = "proto3";
option go_package = "./;proto";
// 定义一个打招呼服务
service Greeter {
// SayHello 方法
rpc SayHello (HelloRequest) returns (HelloReply);
}
//请求消息
message HelloRequest {
string name = 1;
}
//响应消息
message HelloReply {
string message = 1;
}
执行命令生成go文件:
protoc -I . helloworld.proto --go_out=plugins=grpc:.
编写server:
package main
import (
"context"
"fmt"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
"net"
"grpctest/proto"
)
type server struct{}
func (s *server) SayHello(ctx context.Context, request *proto.HelloRequest) (*proto.HelloReply, error) {
return &proto.HelloReply{Message: "Hello " + request.Name}, nil
}
func main() {
s := grpc.NewServer() // 创建gRPC服务器
proto.RegisterGreeterServer(s, &server{}) // 在gRPC服务端注册服务
// 监听本地的18866端口
lis, err := net.Listen("tcp", ":18866")
if err != nil {
fmt.Printf("failed to listen: %v", err)
return
}
reflection.Register(s) //在给定的gRPC服务器上注册服务器反射服务
// Serve方法在lis上接受传入连接,为每个连接创建一个ServerTransport和server的goroutine。
// 该goroutine读取gRPC请求,然后调用已注册的处理程序来响应它们。
err = s.Serve(lis)
if err != nil {
fmt.Printf("failed to serve: %v", err)
return
}
}
编写client:
package main
import (
"context"
"fmt"
"google.golang.org/grpc"
"grpctest/proto"
)
func main() {
// 连接服务器
conn, err := grpc.Dial(":18866", grpc.WithInsecure())
if err != nil {
fmt.Printf("faild to connect: %v", err)
}
defer conn.Close()
c := proto.NewGreeterClient(conn)
// 调用服务端的SayHello
r, err := c.SayHello(context.Background(), &proto.HelloRequest{Name: "dark"})
if err != nil {
fmt.Printf("could not greet: %v", err)
}
fmt.Printf("Greeting: %s !\n", r.Message)
}
工程结构:
grpctest
├── client
│ └── client.go
├── go.mod
├── go.sum
├── main.go
├── proto
│ ├── helloworld.pb.go
│ ├── helloworld.proto
│ ├── student.pb.go
│ └── student.proto
└── server
└── server.go