下载protoc-3.19.3-win64.zip
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
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