开发环境
OS: mac OS 14.2.1 (23C71)
Golang: go version go1.21.0 darwin/arm64
Protoc: libprotoc 3.20.3
Hello World Demo
创建目录
1 2 3
| mkdir -p go-grpc/helloworld cd go-grpc go mod init go-grpc
|
编写 Protocol Buffers 协议文件
创建 helloworld/helloworld.proto
文件并编辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| // gRPC: helloworld demo syntax = "proto3";
option go_package = "./helloworld";
// The greeting service definition. service Greeter { // Send a greeting rpc SayHello (HelloRequest) returns (HelloReply) {} }
// The resquest message containing the user's name. message HelloRequest { string name = 1; }
// The response message containing the greetings. message HelloReply { string message = 1; }
|
将 Protocol Buffers 文件转换为 Go 语言的源代码文件
使用以下命令将helloworld/helloworld.proto
转换成go
语言源代码文件:
1
| protoc -I . --go_out=. --go-grpc_out=require_unimplemented_servers=false:. helloworld/helloworld.proto
|
- protoc:这是 Protocol Buffers 编译器的命令,用于编译 .proto 文件。
- -I .:指定了编译器查找导入文件的目录,. 表示当前目录。
- –go_out=.:这部分指令告诉编译器生成 Go 语言的代码,并将输出文件放在当前目录中。
- –go-grpc_out=require_unimplemented_servers=false:.:这部分指令告诉编译器生成支持 gRPC 的 Go 语言代码,并且在生成的代码中禁用未实现的服务器检查。同样,输出文件也会放在当前目录中。
- helloworld/helloworld.proto:这是要编译的 Protocol Buffers 文件路径。
执行完成后会生成一个:helloworld.pb.go
、helloworld.proto
文件, 目录结构如下:
1 2 3 4
| helloworld ├── helloworld.pb.go ├── helloworld.proto └── helloworld_grpc.pb.go
|
编写 gRPC 服务端的代码
在工程目录下创建并编辑 server/main.go
文件,内容为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
|
package main
import ( "context" "log" "net"
"google.golang.org/grpc" "google.golang.org/grpc/reflection"
pb "go-grpc/helloworld" )
const port = "0.0.0.0:50051"
type server struct { pb.UnimplementedGreeterServer }
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() pb.RegisterGreeterServer(s, &server{}) reflection.Register(s) if err := s.Serve(lis); err != nil { log.Fatalf("failed to server: %v", err) } }
|
编写 gRPC 客户端的代码
在工程目录下创建并编辑client/main.go
文件,内容为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
|
package main
import ( "context" "log" "os" "time"
"google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure"
pb "go-grpc/helloworld" )
const ( address = "localhost:50051" defaultName = "world" )
func main() { conn, err := grpc.Dial(address, grpc.WithTransportCredentials(insecure.NewCredentials())) 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] } ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel()
r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name}) if err != nil { log.Fatalf("could not greet: %v", err) } log.Printf("Greeting: %v", r.Message) }
|
Try it out!
在工程目录下执行以下命令运行服务端:
1 2
| > go mod tidy > go run server/main.go
|
开启一个新的终端并启动客户端,如果没有报错可以看到有调用成功的结果输出:
1 2
| > go run client/main.go 2023/12/31 23:50:15 Greeting: Hello world
|
协议变更
gRPC
协议旨在支持随时间变化的服务。 通常,gRPC
服务和方法会不中断地新增内容。
更新 gRPC 服务
给 helloworld/helloworld.proto
再添加一个方法:SayHelloAgain
, request
和 response
类型同 SayHello
函数。
1 2 3 4 5 6 7 8
| ... // The greeting service definition. service Greeter { ... // Sends another greeting rpc SayHelloAgain (HelloRequest) returns (HelloReply) {} } ...
|
重新生成 gRPC 代码
重新将变更后的helloworld/helloworld.proto
转换成go
语言源代码文件:
1
| protoc -I . --go_out=. --go-grpc_out=require_unimplemented_servers=false:. helloworld/helloworld.proto
|
更新服务端
编辑 server/main.go
文件,添加以下代码:
1 2 3
| func (s *server) SayHelloAgain(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { return &pb.HelloReply{Message: "Hello again " + in.Name}, nil }
|
更新客户端
编辑 client/main.go
文件,添加以下代码:
1 2 3 4 5
| r, err = c.SayHelloAgain(ctx, &pb.HelloRequest{Name: name}) if err != nil { log.Fatalf("could not greet: %v", err) } log.Printf("Greeting: %s", r.Message)
|
运行
执行以下命令重跑服务端:
启动客户端,可以看到新方法调用成功的结果输出:
1 2 3
| > go run client/main.go 2023/12/31 00:08:44 Greeting: Hello world 2023/12/31 00:08:44 Greeting: Hello again world
|
引用
以上。