如何使用Golang实现gRPC通信_gRPC通信实现步骤说明

15次阅读

Protocol Buffer 接口必须在 .proto 文件中正确定义,包括 syntax、package、service 和 message;需用 protoc-gen-go 与 protoc-gen-go-grpc 插件生成代码;Server 必须调用 registerXXXServer 并监听;Client 需复用连接并设置 context 超时。

如何使用Golang实现gRPC通信_gRPC通信实现步骤说明

定义 Protocol Buffer 接口(.proto 文件)

gRPC 的核心是接口定义必须先写在 .proto 文件里,Go 代码只是生成结果。不写对这个文件,后续所有步骤都会失败。

常见错误:直接写 Go 结构体当服务接口、漏写 syntax = "proto3";、在 service 中用 rpc 声明方法但没配对的 message 请求/响应类型。

  • 必须声明 package,它会映射为 Go 的包名(如 package helloworld; → 生成代码在 helloworld 目录)
  • 服务方法参数和返回值必须是 message 类型,不能是基础类型(如 Stringint32 单独作参数)
  • 若需流式通信,用 stream 关键字,例如 rpc SayHelloStream(stream HelloRequest) returns (stream HelloReply);
syntax = "proto3";  package helloworld;  service Greeter {   rpc SayHello (HelloRequest) returns (HelloReply); }  message HelloRequest {   string name = 1; }  message HelloReply {   string message = 1; }

用 protoc 生成 Go 代码

Go 不自带 protoc 插件,必须手动安装 protoc-gen-goprotoc-gen-go-grpc,否则 protoc 会报错“Plugin failed with status code 1”。

生成命令容易漏掉 --go-grpc_out —— 只用 --go_out 只生成数据结构,不生成 gRPC Server/Client 接口,运行时会提示 undefined: helloworld.NewGreeterClient 这类错误。

立即学习go语言免费学习笔记(深入)”;

  • 安装插件:go install google.golang.org/protobuf/cmd/protoc-gen-go@latestgo install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
  • 执行生成(确保当前目录有 helloworld.proto):
    protoc --go_out=. --go-grpc_out=. --go-grpc_opt=paths=source_relative helloworld.proto
  • 生成后会得到两个文件:helloworld/helloworld.pb.go(消息结构)和 helloworld/helloworld_grpc.pb.go(客户端接口、服务端抽象接口)

实现 gRPC Server(基于 net.Listener)

Server 启动依赖 net.Listen + grpc.NewServer() + RegisterXXXServer 三步缺一不可。最容易忽略的是没调用 RegisterXXXServer,导致客户端调用时返回 UNIMPLEMENTED 错误。

另一个坑是未处理监听地址冲突(如端口被占),net.Listen 失败后没检查 Error,程序静默 panic 或卡住。

  • grpc.NewServer() 可传入选项,例如启用 TLS:grpc.Creds(credentials.NewTLS(...))
  • 注册服务必须用生成的 RegisterGreeterServer 函数,不是手写的结构体指针直传
  • 启动后需阻塞等待连接,常用 fmt.Scanln()signal.Notify 捕获中断
package main  import (     "context"     "log"     "net"      "google.golang.org/grpc"     pb "your-module/helloworld" )  type server Struct {     pb.UnimplementedGreeterServer }  func (s *server) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {     return &pb.HelloReply{Message: "Hello " + req.Name}, nil }  func main() {     lis, err := net.Listen("tcp", ":50051")     if err != nil {         log.Fatalf("failed to listen: %v", err)     }     s := grpc.NewServer()     pb.RegisterGreeterServer(s, &server{})     log.Println("gRPC server listening on :50051")     if err := s.Serve(lis); err != nil {         log.Fatalf("failed to serve: %v", err)     } }

编写 gRPC Client 并处理连接生命周期

Client 端最常出问题的是连接没关闭、没设超时、或复用已关闭的 *grpc.ClientConn。一旦连接断开又继续调用,会报 rpc error: code = Unavailable desc = closing transport due to: connection error

不要把 grpc.Dial 写在每次 RPC 调用前——它开销大且可能泄漏连接;也不要在 defer 中无条件 Close(),因为连接通常要复用。

  • 推荐用 grpc.WithTransportCredentials(insecure.NewCredentials()) 开发时跳过 TLS(生产环境必须配证书)
  • 务必设置上下文超时:ctx, cancel := context.WithTimeout(context.background(), 5*time.Second),再传给 RPC 方法
  • 连接应作为长生命周期对象管理(如封装进 struct),避免频繁 Dial/Close
package main  import (     "context"     "log"     "time"      "google.golang.org/grpc"     "google.golang.org/grpc/credentials/insecure"     pb "your-module/helloworld" )  func main() {     conn, err := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(insecure.NewCredentials()))     if err != nil {         log.Fatalf("did not connect: %v", err)     }     defer conn.Close()      client := pb.NewGreeterClient(conn)     ctx, cancel := context.WithTimeout(context.Background(), time.Second)     defer cancel()      r, err := client.SayHello(ctx, &pb.HelloRequest{Name: "World"})     if err != nil {         log.Fatalf("could not greet: %v", err)     }     log.Printf("Greeting: %s", r.GetMessage()) }

实际跑通的关键不在语法多炫,而在 .proto 定义是否闭环、protoc 插件是否齐全、Server 是否完成 Register、Client 是否正确携带 context 超时——这四点任一缺失,都会让请求卡在无声的失败里。

text=ZqhQzanResources