go语言 gmp Go语言中gPRC的使用 go语言gorm

go语言 gmp Go语言中gPRC的使用 go语言gorm

目录
  • 核心概念领会
    • 1. Protobuf 与服务定义
    • 2. 代码生成
    • 3. 服务端实现
    • 4. 服务启动
    • 5. 客户端调用
  • 与标准库 RPC 的区别
    • 客户端异步调用
      • gPRC流
        • 1. 为什么需要 gRPC 流?
        • 2. 定义流服务
        • 3. 流接口详解
        • 4. 服务端实现
        • 5. 客户端实现
        • 6. 流的职业模式
        • 7. 流的特性
        • 8. 适用场景
        • 9. 注意事项
      • gRPC发布-订阅
        • 本地发布-订阅实现
      • 基于gRPC的远程发布-订阅体系
        • 1. 定义服务接口
        • 2. 服务端实现
        • 3. 客户端发布消息
        • 4. 客户端订阅消息

      核心概念领会

      1. Protobuf 与服务定义

      gRPC 使用 Protobuf 来定义服务接口和消息格式。在你的例子中,hello.proto 文件定义了一个名为 HelloService 的服务,它包含一个技巧 Hello,该技巧接收一个 String 消息并返回一个 String 消息。

      service HelloService rpc Hello (String) returns (String);}

      这类似于定义一个函数接口,但它是跨网络调用的。

      2. 代码生成

      通过 Protobuf 编译器和 gRPC 插件,你可以生成客户端和服务器的代码:

      protoc –go_out=plugins=grpc:. hello.proto

      这行命令会生成:

      • 客户端代码:包含怎样调用远程服务的技巧
      • 服务器代码:包含怎样实现服务的接口定义

      3. 服务端实现

      服务端需要实现 HelloServiceServer 接口:

      type HelloServiceImpl struct}func (p HelloServiceImpl) Hello(ctx context.Context, args String) (String, error) reply := &StringValue: “hello:” + args.GetValue()} return reply, nil}

      这里的 Hello 技巧是服务的具体实现,它接收一个字符串参数,添加 “hello:” 前缀后返回。

      4. 服务启动

      服务端需要启动 gRPC 服务器并注册服务:

      func main() grpcServer := grpc.NewServer() RegisterHelloServiceServer(grpcServer, new(HelloServiceImpl)) lis, err := net.Listen(“tcp”, “:1234”) if err != nil log.Fatal(err) } grpcServer.Serve(lis)}

      这部分代码:

      • 创建 gRPC 服务器
      • 注册服务实现
      • 监听 TCP 端口
      • 启动服务

      5. 客户端调用

      客户端需要连接到服务端并调用服务:

      func main() conn, err := grpc.Dial(“localhost:1234”, grpc.WithInsecure()) if err != nil log.Fatal(err) } defer conn.Close() client := NewHelloServiceClient(conn) reply, err := client.Hello(context.Background(), &StringValue: “hello”}) if err != nil log.Fatal(err) } fmt.Println(reply.GetValue())}

      这部分代码:

      • 连接到服务端
      • 创建客户端
      • 调用远程技巧
      • 处理返回结局

      与标准库 RPC 的区别

      • 协议:gRPC 使用 HTTP/2,而标准库 RPC 使用自定义协议
      • 序列化:gRPC 使用 Protobuf (二进制),标准库 RPC 使用 JSON 或 Gob
      • 性能:gRPC 通常更快,尤其是在高并发场景
      • 跨语言支持:gRPC 支持多种语言,标准库 RPC 主要支持 Go
      • 流模式:gRPC 支持双向流,标准库 RPC 不支持

      客户端异步调用

      你提到 gRPC 不直接支持异步调用,但可以通过 Goroutine 实现:

      // 客户端异步调用示例go func() reply, err := client.Hello(context.Background(), &StringValue: “async”}) if err != nil log.Println(“Error:”, err) return } fmt.Println(“Async reply:”, reply.GetValue())}()

      gPRC流

      1. 为什么需要 gRPC 流?

      传统 RPC 调用是"请求-响应"模式:

      • 客户端发送一个请求
      • 服务器处理请求并返回一个响应
      • 整个经过是同步的,不适合大数据传输或实时通信

      gRPC 流解决了这些难题,它允许:

      • 客户端和服务器之间持续交换数据
      • 支持四种流模式:
        • 客户端流(单向)
        • 服务器流(单向)
        • 双向流
        • 一元(普通 RPC,无流)

      2. 定义流服务

      .proto 文件中,使用 stream 关键字定义流:

      service HelloService // 普通 RPC(一元) rpc Hello (String) returns (String); // 双向流 RPC rpc Channel (stream String) returns (stream String);}

      这里的 Channel 技巧支持双向流:

      • 客户端可以持续发送 String 消息
      • 服务器可以持续返回 String 消息

      3. 流接口详解

      生成的代码中,流接口包含 SendRecv 技巧:

      // 服务端流接口type HelloService_ChannelServer interface Send(String) error // 向客户端发送消息 Recv() (String, error) // 从客户端接收消息 grpc.ServerStream}// 客户端流接口type HelloService_ChannelClient interface Send(String) error // 向服务器发送消息 Recv() (String, error) // 从服务器接收消息 grpc.ClientStream}

      这些技巧允许双向、异步的数据交换。

      4. 服务端实现

      服务端实现 Channel 技巧:

      func (p HelloServiceImpl) Channel(stream HelloService_ChannelServer) error for // 接收客户端消息 args, err := stream.Recv() if err != nil if err == io.EOF return nil // 客户端关闭流 } return err // 处理其他错误 } // 处理消息并返回响应 reply := &StringValue: “hello:” + args.GetValue()} err = stream.Send(reply) if err != nil return err } }}

      关键点:

      • stream.Recv() 从客户端接收消息
      • stream.Send() 向客户端发送消息
      • io.EOF 表示客户端关闭了流

      5. 客户端实现

      客户端需要启动两个 Goroutine 分别处理发送和接收:

      // 创建流stream, err := client.Channel(context.Background())if err != nil log.Fatal(err)}// 发送消息的 Goroutinego func() for if err := stream.Send(&StringValue: “hi”}); err != nil log.Fatal(err) } time.Sleep(time.Second) // 每秒发送一次 }}()// 接收消息的主循环for reply, err := stream.Recv() if err != nil if err == io.EOF break // 服务器关闭流 } log.Fatal(err) } fmt.Println(“Received:”, reply.GetValue())}

      关键点:

      • 发送和接收是完全独立的
      • 客户端和服务器可以随时发送数据
      • io.EOF 表示服务器关闭了流

      6. 流的职业模式

      gRPC 流有四种职业模式:

      1. 一元 RPC(无流)

      rpc UnaryMethod(Request) returns (Response) }

      • 客户端发送一个请求,服务器返回一个响应

      2. 服务器流

      rpc ServerStreamingMethod(Request) returns (stream Response) }

      • 客户端发送一个请求
      • 服务器返回一个数据流
      • 示例:实时数据推送

      3. 客户端流

      rpc ClientStreamingMethod(stream Request) returns (Response) }

      • 客户端发送一个数据流
      • 服务器返回一个响应
      • 示例:大数据上传

      4. 双向流

      rpc BidirectionalStreamingMethod(stream Request) returns (stream Response) }

      • 客户端和服务器可以同时发送和接收数据流
      • 示例:实时聊天、多人游戏

      7. 流的特性

      1. 全双工通信:客户端和服务器可以同时发送和接收数据
      2. 异步处理:发送和接收操作不阻塞其他操作
      3. 高效传输:基于 HTTP/2 的多路复用,单个连接支持多个流
      4. 大数据支持:适合传输大文件或持续数据流
      5. 实时性:数据可以立即传输,无需等待整个消息完成

      8. 适用场景

      • 实时数据推送(股票行情、实时通知)
      • 大数据上传/下载
      • 实时聊天体系
      • 监控体系(持续数据流)
      • 游戏服务器(低延迟通信)

      9. 注意事项

      1. 错误处理:流可能因网络难题或服务器关闭而中断,需要适当处理错误
      2. 资源管理:流使用完后需要关闭,避免资源泄漏
      3. 并发控制:多个 Goroutine 访问同一个流时需要考虑同步难题
      4. 消息顺序:在双向流中,发送和接收的顺序可能不同步

      gRPC发布-订阅

      发布-订阅(PubSub)模式是一种消息传递模式,其中发送者(发布者)不会直接将消息发送给特定的接收者(订阅者),而是将消息分类发布到主题中。订阅者可以订阅一个或多个主题,只接收他们感兴趣的消息。这种模式实现了发布者和订阅者之间的解耦,非常适合构建分布式体系。

      本地发布-订阅实现

      开门见山说,我们看一下基于 moby/moby/pkg/pubsub 包的本地实现:

      import ( “github.com/moby/moby/pkg/pubsub”)func main() // 创建发布者,设置超时和队列大致 p := pubsub.NewPublisher(100time.Millisecond, 10) // 订阅golang主题 golang := p.SubscribeTopic(func(v interface}) bool if key, ok := v.(string); ok return strings.HasPrefix(key, “golang:”) } return false }) // 订阅docker主题 docker := p.SubscribeTopic(func(v interface}) bool if key, ok := v.(string); ok return strings.HasPrefix(key, “docker:”) } return false }) // 发布消息 go p.Publish(“hi”) go p.Publish(“golang: https://golang.org”) go p.Publish(“docker: https://www.docker.com/”) // 接收消息 go func() fmt.Println(“golang topic:”, <-golang) }() go func() fmt.Println(“docker topic:”, <-docker) }() <-make(chan bool) // 保持主程序运行}

      关键点:

      • pubsub.NewPublisher 创建一个发布者,负责管理主题和分发消息
      • SubscribeTopic 使用谓词函数过滤感兴趣的主题
      • 发布者发布消息时,所有订阅该主题的订阅者都会收到通知
      • 这一个本地实现,无法跨网络职业

      基于gRPC的远程发布-订阅体系

      现在,我们将使用gRPC扩展这个体系,使其能够跨网络职业。

      1. 定义服务接口

      service PubsubService rpc Publish (String) returns (String); // 发布消息 rpc Subscribe (String) returns (stream String); // 订阅主题}

      这里:

      • Publish 一个普通的RPC技巧,用于发布消息
      • Subscribe 一个服务端流技巧,允许客户端持续接收消息

      2. 服务端实现

      type PubsubService struct pub pubsub.Publisher}func NewPubsubService() PubsubService return &PubsubService pub: pubsub.NewPublisher(100time.Millisecond, 10), }}// 发布消息func (p PubsubService) Publish(ctx context.Context, arg String) (String, error) p.pub.Publish(arg.GetValue()) return &String}, nil}// 订阅主题func (p PubsubService) Subscribe(arg String, stream PubsubService_SubscribeServer) error // 订阅特定主题 ch := p.pub.SubscribeTopic(func(v interface}) bool if key, ok := v.(string); ok return strings.HasPrefix(key, arg.GetValue()) } return false }) // 将接收到的消息通过流发送给客户端 for v := range ch if err := stream.Send(&StringValue: v.(string)}); err != nil return err } } return nil}

      关键点:

      • 服务端维护一个 pubsub.Publisher 实例
      • Publish 技巧将消息发布到本地发布者
      • Subscribe 技巧创建一个主题订阅,并通过gRPC流将消息发送给客户端

      3. 客户端发布消息

      func main() conn, err := grpc.Dial(“localhost:1234”, grpc.WithInsecure()) if err != nil log.Fatal(err) } defer conn.Close() client := NewPubsubServiceClient(conn) // 发布golang主题消息 _, err = client.Publish(context.Background(), &StringValue: “golang: hello Go”}) if err != nil log.Fatal(err) } // 发布docker主题消息 _, err = client.Publish(context.Background(), &StringValue: “docker: hello Docker”}) if err != nil log.Fatal(err) }}

      4. 客户端订阅消息

      func main() conn, err := grpc.Dial(“localhost:1234”, grpc.WithInsecure()) if err != nil log.Fatal(err) } defer conn.Close() client := NewPubsubServiceClient(conn) // 订阅golang主题 stream, err := client.Subscribe(context.Background(), &StringValue: “golang:”}) if err != nil log.Fatal(err) } // 持续接收消息 for reply, err := stream.Recv() if err != nil if err == io.EOF break } log.Fatal(err) } fmt.Println(reply.GetValue()) }}

      职业原理详解

      服务端

      • 创建一个本地的 pubsub.Publisher 实例
      • Publish 技巧将接收到的消息发布到本地发布者
      • Subscribe 技巧创建一个主题订阅,并通过gRPC流将消息实时发送给客户端

      客户端发布

      • 调用远程的 Publish 技巧
      • 消息通过gRPC传输到服务端
      • 服务端将消息发布到本地主题

      客户端订阅

      • 调用远程的 Subscribe 技巧,建立一个服务端流
      • 服务端为该客户端创建一个主题订阅
      • 当有匹配的消息发布时,服务端通过流将消息发送给客户端
      • 客户端持续接收并处理这些消息

      优势与应用场景

      这种基于gRPC的发布-订阅体系具有下面内容优势:

      • 跨语言支持:gRPC支持多种语言,可以构建多语言混合的分布式体系
      • 高性能:基于HTTP/2和Protobuf,性能优越
      • 实时通信:使用流特性实现实时消息推送
      • 解耦:发布者和订阅者不需要直接交互
      • 扩展性:可以轻松扩展为支持多个主题和大量订阅者

      典型应用场景包括:

      • 实时数据推送(如股票行情、新闻更新)
      • 微服务间的事件驱动通信
      • 实时聊天体系
      • 监控和警报体系

      通过这种方式,你可以构建一个跨网络的、高效的发布-订阅体系,充分利用gRPC的流特性和类型安全优势。

      到此这篇关于Go语言中gPRC的使用的文章就介绍到这了,更多相关Go语言 gPRC内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!

      无论兄弟们可能感兴趣的文章:

      • Go语言string,int,int64 ,float之间类型转换技巧
      • Go语言中的Array、Slice、Map和Set使用详解
      • Go语言转换所有字符串为大写或者小写的技巧
      • Go语言interface详解
      • Go语言创建、初始化数组的常见方式汇总
      • 深入讲解Go语言中函数new与make的使用和区别
      • Go语言中字符串的查找技巧
      • GO语言并发编程之互斥锁、读写锁详解
      • Go语言运行环境安装详细教程
      • Go语言常用字符串处理技巧实例汇总
      版权声明

      返回顶部