这是我参与「第五届青训营 」伴学笔记创作活动的第 6 天
1 Protobuf
1.1 指定 protobuf 版本
syntax = "proto3";
1.2 指定 protobuf 包
option go_package = ".;proto";
不建议使用:
package proto; // option go_package = ".;proto"; 会覆盖此选项
1.3 定义 message
message WorldRequest{
string s = 1; // 需要传递的消息
}
message WorldResponse{
int32 status_code = 1; // 状态码,0-成功,其他值-失败
string status_msg = 2; // 返回状态描述
}
message 由 message 字段定义,后面紧跟需要定义的消息名称。message 由若干字段组成,每个字段由 类型 字段名=序号 组成。该序号在客户端和服务端需要和字段名一一对应,否则将解析失败。
1.4 定义 rpc 服务
service Hello{
rpc World(WordRequest) returns (WordResponse);
rpc Go(GoRequest) returns (GoResponse);
}
service 由 service 字段定义, 后面紧跟需要定义的服务名称。service 由若干个调用组成,每个调用由 rpc 调用名称(调用参数) returns (返回参数) 组成,其中参数为 message 定义的消息。
1.5 生成 GO 文件
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative helloworld.proto
--go_out=. 和 --go-grpc_out=. 分别指明 go 和 go-grpc 文件生成路径。
2 Grpc
2.1 (srv)注册 Grpc 服务
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", 8080))
if err != nil {
zap.S().Infof("failed to listen: %v", err)
}
zap.S().Infof("server listening at %v", lis.Addr())
if err := s.Serve(lis); err != nil {
zap.S().Infof("failed to serve: %v", err)
}
2.2 (cli)请求 Grpc 服务
func client() {
conn, err := net.Dial("tcp", "localhost:8080")
if err != nil {
fmt.Println("Error: ", err)
return
}
reply := ""
args := fmt.Sprintf("Goodbye %d", n)
client := rpc.NewClientWithCodec(jsonrpc.NewClientCodec(conn))
err = client.Call("hello.Hello", args, &reply)
if err != nil {
fmt.Println("Error: ", err)
return
}
fmt.Println("Reply: ", reply)
return
}
2.3 健康检查/心跳检测
healthcheck := health.NewServer() // 注册健康检查
healthgrpc.RegisterHealthServer(s, healthcheck)
go func() {
next := healthpb.HealthCheckResponse_SERVING
for {
healthcheck.SetServingStatus("user", next)
if next == healthpb.HealthCheckResponse_SERVING {
next = healthpb.HealthCheckResponse_NOT_SERVING
} else {
next = healthpb.HealthCheckResponse_SERVING
}
time.Sleep(1 * time.Second)
}
}()