boxmoe_header_banner_img

Hello! 欢迎来到悠悠畅享网!

文章导读

怎样用Golang开发GRPC服务 定义proto文件与生成代码


avatar
作者 2025年8月24日 18

编写清晰的 .proto 文件需定义 syntax、package、service 和 message,使用 proto3 语法声明服务接口与消息类型,如 Greeter 服务包含 SayHello 方法;通过 protoc 生成 Go 代码后,在服务端实现接口逻辑并启动 grpc 服务器,客户端创建连接并调用方法;错误处理使用 status 包返回错误码与消息;流式传输通过在 .proto 中定义 stream 关键字实现双向流通信。

怎样用Golang开发GRPC服务 定义proto文件与生成代码

golang 开发 gRPC 服务,核心在于定义

.proto

文件来描述服务接口,然后使用

protoc

编译器生成 Golang 代码,包括服务接口和消息类型。

定义

.proto

文件并生成代码,再编写服务端和客户端逻辑。

如何编写清晰的

.proto

文件来定义 gRPC 服务?

首先,你需要安装

protoc

编译器和 Golang 的 gRPC 插件。 然后,创建一个

.proto

文件,比如

service.proto

,在里面定义你的服务接口和消息类型。

syntax = "proto3";  package example;  service Greeter {   rpc SayHello (HelloRequest) returns (HelloReply) {} }  message HelloRequest {   string name = 1; }  message HelloReply {   string message = 1; }

这个例子定义了一个

Greeter

服务,包含一个

SayHello

方法,接收

HelloRequest

消息,返回

HelloReply

消息。

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

接下来,使用

protoc

编译器生成 Golang 代码:

protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative service.proto

这会生成

service.pb.go

service_grpc.pb.go

两个文件,包含定义的消息类型和服务接口。

如何实现 gRPC 服务端?

有了生成的代码,就可以编写服务端逻辑了。 创建一个

server.go

文件:

package main  import (     "context"     "fmt"     "log"     "net"      "google.golang.org/grpc"     pb "example" // 替换为你的包名 )  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{})     fmt.Println("Server listening on :50051")     if err := s.Serve(lis); err != nil {         log.Fatalf("failed to serve: %v", err)     } }

这个服务端实现了

Greeter

接口的

SayHello

方法,并监听

50051

端口。

如何编写 gRPC 客户端?

类似地,创建一个

client.go

文件:

package main  import (     "context"     "log"     "os"     "time"      "google.golang.org/grpc"     "google.golang.org/grpc/credentials/insecure"     pb "example" // 替换为你的包名 )  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: %s", r.Message) }

这个客户端连接到服务端,调用

SayHello

方法,并打印返回的消息。

如何处理 gRPC 服务的错误?

gRPC 错误处理通常通过返回

error

类型来实现。 在服务端,你可以返回

status.Error

来传递更详细的错误信息,例如错误码和错误消息。

import (     "context"     "fmt"     "log"     "net"      "google.golang.org/grpc"     "google.golang.org/grpc/status"     "google.golang.org/grpc/codes"     pb "example" // 替换为你的包名 )  type server struct {     pb.UnimplementedGreeterServer }  func (s *server) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {     if req.Name == "" {         return nil, status.Error(codes.InvalidArgument, "Name cannot be empty")     }     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{})     fmt.Println("Server listening on :50051")     if err := s.Serve(lis); err != nil {         log.Fatalf("failed to serve: %v", err)     } }

在客户端,你可以检查返回的

error

是否为

status.Error

,并获取错误码和错误消息。

import (     "context"     "log"     "os"     "time"      "google.golang.org/grpc"     "google.golang.org/grpc/credentials/insecure"     "google.golang.org/grpc/status"     pb "example" // 替换为你的包名 )  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 {         st, ok := status.FromError(err)         if ok {             log.Printf("gRPC error: code=%s, message=%s", st.Code(), st.Message())         } else {             log.Fatalf("could not greet: %v", err)         }         return     }     log.Printf("Greeting: %s", r.Message) }

如何使用 gRPC 的 Stream 功能?

gRPC 支持流式传输,允许服务端和客户端双向发送多个消息。 在

.proto

文件中,可以使用

stream

关键字定义流式 RPC。

syntax = "proto3";  package example;  service Greeter {   rpc SayHello (HelloRequest) returns (HelloReply) {}   rpc SayHelloStream (stream HelloRequest) returns (stream HelloReply) {} }  message HelloRequest {   string name = 1; }  message HelloReply {   string message = 1; }

这里定义了一个

SayHelloStream

方法,接收一个

HelloRequest

消息流,返回一个

HelloReply

消息流。

服务端实现:

func (s *server) SayHelloStream(stream pb.Greeter_SayHelloStreamServer) error {     for {         req, err := stream.Recv()         if err == io.EOF {             return nil         }         if err != nil {             return err         }         err = stream.Send(&pb.HelloReply{Message: "Hello " + req.Name})         if err != nil {             return err         }     } }

客户端实现:

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)      stream, err := c.SayHelloStream(context.Background())     if err != nil {         log.Fatalf("could not greet: %v", err)     }      for i := 0; i < 5; i++ {         err := stream.Send(&pb.HelloRequest{Name: fmt.Sprintf("world %d", i)})         if err != nil {             log.Fatalf("could not send: %v", err)         }         resp, err := stream.Recv()         if err == io.EOF {             break         }         if err != nil {             log.Fatalf("could not receive: %v", err)         }         log.Printf("Greeting: %s", resp.Message)     }      err = stream.CloseSend()     if err != nil {         log.Fatalf("could not close: %v", err)     } }



评论(已关闭)

评论已关闭