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

编写清晰的 .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 mainimport (    "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 mainimport (    "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)    }}

以上就是怎样用Golang开发GRPC服务 定义proto文件与生成代码的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1400033.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月15日 16:41:59
下一篇 2025年12月15日 16:42:13

相关推荐

  • Golang如何创建自定义错误 实现带有堆栈信息的错误类型

    自定义错误需捕获堆栈信息以提升调试效率,可通过实现包含消息和调用栈的结构体,利用runtime.Callers和FuncForPC获取帧信息,或使用github.com/pkg/errors等库简化操作。 在Go语言中,创建带有堆栈信息的自定义错误可以极大提升调试效率,尤其是在复杂调用链中定位问题时…

    好文分享 2025年12月15日
    000
  • 如何降级Golang依赖版本 解决版本冲突的回退方案

    降级Golang依赖版本需修改go.mod文件中的版本号并运行go mod tidy,以解决依赖冲突或适配环境;若构建失败,可能因代码不兼容旧版本API、传递性依赖冲突、模块缓存未清理或vendor目录未同步;可通过go clean -modcache清除缓存、删除vendor后重新生成来解决;强制…

    2025年12月15日
    000
  • Golang如何实现深拷贝 值类型与指针类型区别

    深拷贝需创建完全独立的副本,避免原对象被修改。Go中通过手动复制、Gob序列化或第三方库实现,注意循环引用、不可导出字段及性能开销,选择方法需权衡控制粒度、通用性与便捷性。 在Go语言中,深拷贝是指创建一个新对象,其字段值与原对象完全相同,并且所有嵌套的引用类型(如指针、切片、map等)也都是独立的…

    2025年12月15日
    000
  • Golangcgo性能调优 减少CGo边界开销

    CGo边界开销源于Go与C运行时模型差异,导致每次调用需上下文切换、内存同步和栈转换,核心优化策略包括:1. 批量处理调用,减少跨语言调用次数;2. 使用unsafe.Pointer传递指针,避免数据拷贝,结合runtime.KeepAlive防止GC过早回收;3. 复用长生命周期C对象,降低初始化…

    2025年12月15日
    000
  • 怎样为Golang搭建AI训练集群 配置Kubeflow分布式训练

    为Golang搭建AI训练集群并集成Kubeflow,需先构建Kubernetes集群,再部署Kubeflow组件,接着将Go训练代码通过Dockerfile容器化,最后利用Kubeflow Pipelines的Python SDK定义任务流程,调用Go镜像执行训练,实现高性能与MLOps的融合。 …

    2025年12月15日
    000
  • Golang字符串操作常用方法 掌握strings包核心函数

    Go语言strings包提供高效字符串操作,涵盖查找(Contains、Index)、替换(ReplaceAll)、分割(Split)、拼接(Join)、清理(TrimSpace)和格式化(ToLower/ToUpper)等核心功能,适用于日常文本处理,提升代码简洁性与性能。 在Go语言里,处理字符…

    2025年12月15日
    000
  • Golang命令模式开发 将请求封装为对象

    命令模式通过封装请求为对象实现调用者与接收者解耦,支持撤销、队列和扩展,适用于Go语言中的遥控操作、任务队列等场景。 在Go语言开发中,命令模式是一种行为设计模式,它将请求封装为对象,从而使你可以用不同的请求、队列或日志来参数化其他对象。命令模式的核心思想是将“执行某个操作”的请求抽象成一个独立的命…

    2025年12月15日
    000
  • 如何创建可复用的Golang包 详解导出规则与internal包用法

    Go语言通过首字母大小写控制标识符导出,大写可导出,小写为私有;internal包限制仅父模块可导入,实现细粒度访问控制,适用于模块内部逻辑拆分与封装,配合单元测试和集成测试确保代码质量。 创建可复用的Golang包,核心在于理解其导出规则和 internal 包的独特用法。简单来说,Go语言通过标…

    2025年12月15日
    000
  • Golang并发调试方法 delve调试器使用

    Delve 是调试 Go 并发程序的核心工具,支持查看和切换 goroutine、设置条件断点、结合 -race 检测竞态。使用 dlv debug 启动程序后,通过 goroutines 命令列出所有协程,goroutine ID 切换上下文,bt 和 print 分析调用栈与变量。可设置 goi…

    2025年12月15日
    000
  • 怎样用Golang实现文件上传 解析multipart/form-data请求

    前端通过enctype=”multipart/form-data”表单上传文件;2. Go服务端用net/http和mime/multipart解析,调用ParseMultipartForm限制大小并获取文件。 处理 multipart/form-data 请求实现文件上传在…

    2025年12月15日
    000
  • Golang实现多云管理平台 统一云服务API抽象层

    构建多云管理平台的核心是通过golang实现统一api抽象层,首先定义计算、存储、网络等通用服务模型,接着设计restful或graphql风格的统一api接口,然后利用golang接口实现适配器模式,为aws、azure等云厂商分别开发适配器以屏蔽底层差异,同时选用gin或echo等web框架提升…

    2025年12月15日
    000
  • Golang的fmt格式化输出 动词使用详解

    Go语言fmt包通过格式动词实现灵活输出:%v默认输出,%+v显示结构体字段名,%#v带类型信息,%T打印类型,%t输出布尔值,%d/%b/%o/%x用于整数不同进制,%f/%e/%g处理浮点数,%s/%q格式化字符串,%p输出指针地址,支持宽度、精度和对齐控制,如%8d右对齐、%-8d左对齐、%.…

    2025年12月15日
    000
  • Golang的net/url网址解析 参数编码解码

    net/url包用于解析和处理URL及查询参数。通过url.Parse()可提取URL各部分;url.ParseQuery()或Query()方法解析查询参数为键值对;url.Values支持参数增删改查并自动编码;QueryEscape/Unescape实现特殊字符编码解码,确保URL合法性。 在…

    2025年12月15日
    000
  • Linux系统中Golang环境如何部署 不同发行版的安装方法

    选择安装方法需权衡版本需求与便捷性,Ubuntu等系统可用apt安装或官网二进制包;若版本过低,可添加PPA、使用snap或手动安装;GOROOT为安装路径,GOPATH为工作区,Module启用后项目可脱离GOPATH,通过go mod init初始化,依赖自动管理,配置PATH包含$GOPATH…

    2025年12月15日
    000
  • Golang并行基准测试 RunParallel方法实践

    使用RunParallel可真实模拟多goroutine并发场景,暴露锁竞争与伸缩性问题。它通过pb.Next()协调所有goroutine共同完成b.N次操作,避免单goroutine串行测试的局限性。默认并行度为GOMAXPROCS,可结合不同CPU核心数测试性能变化,揭示高并发下真实瓶颈。 在…

    2025年12月15日
    000
  • Golang优化容器运行时 gVisor集成

    gVisor通过用户态内核拦截系统调用,提升容器安全性,集成到Go运行时需配置containerd的runtime为runsc,结合OCI规范实现安全与性能平衡。 gVisor 是 Google 开发的一个用户态内核,用于增强容器运行时的安全性。它通过在用户空间实现 Linux 内核接口,拦截并处理…

    2025年12月15日
    000
  • Golang多模块如何管理 workspace模式实践

    Go workspace模式通过go.work文件统一管理多模块项目,解决传统replace指令维护难、本地调试低效、monorepo开发复杂等问题,提升微服务与共享库协同开发效率。 Go语言多模块管理,尤其是在workspace模式下,极大地简化了本地开发和跨模块调试的流程。简单来说,它提供了一个…

    2025年12月15日
    000
  • Golang指针逃逸分析是什么 编译器堆栈分配决策

    逃逸分析是Go编译器确定变量分配在栈或堆上的机制,通过静态分析判断变量生命周期是否超出函数作用域,若会则分配在堆上,否则在栈上以提升性能。 Go语言中的指针逃逸分析(Escape Analysis)是编译器用来决定变量分配在栈上还是堆上的关键机制。它的核心目标是确保内存安全的同时,尽可能提升程序性能…

    2025年12月15日
    000
  • Golang如何优雅处理嵌套错误 使用fmt.Errorf包装

    使用 %w 包装错误可保留原始错误并添加上下文,便于通过 errors.Is 和 errors.As 判断或提取底层错误,应避免无意义的重复包装,确保每层提供有效上下文信息。 在 Go 1.13 之后,fmt.Errorf 支持通过 %w 动词来包装错误,实现嵌套错误的链式处理。这种方式既能保留原始…

    2025年12月15日
    000
  • Google App Engine多语言混合应用部署指南

    Google App Engine支持在同一个应用下部署多个不同语言版本的服务,每个服务作为独立的部署单元,通过唯一的URL访问。这种机制使得开发者能够构建灵活的多语言混合应用,充分利用各语言的优势,实现微服务架构或渐进式系统迁移,而无需将它们拆分为完全独立的应用程序。 Google App Eng…

    2025年12月15日
    000

发表回复

登录后才能评论
关注微信