gRPC 双向流(Bidirectional Streaming RPC)的使用方法

gRPC 是一个支持多种语言的高性能 RPC 框架,拥有丰富的 API 来简化服务端和客户端的开发过程。gRPC 支持四种 RPC 类型:Unary RPC、Server Streaming RPC、Client Streaming RPC 和 Bidirectional Streaming RPC。下面是双向流 API 的使用方法。

双向流 API 示例

在双向流中,客户端和服务器之间可以同时发送和接收消息。以下是一个双向流的完整示例,展示了客户端和服务端之间的聊天功能。

1. 定义 Protobuf 文件

首先,定义一个 Protobuf 文件 chat.proto,描述服务和消息结构:

bash 复制代码
syntax = "proto3";

package chat;

service ChatService {
  // 双向流 RPC
  rpc Chat(stream ChatMessage) returns (stream ChatMessage);
}

message ChatMessage {
  string user = 1;
  string text = 2;
}
2. 编译 Protobuf 文件

使用 protoc 编译器生成 Go 代码:

bash 复制代码
protoc --go_out=paths=source_relative:. --go-grpc_out=paths=source_relative:. chat.proto

这会生成 chat.pb.go 文件,其中包含了定义的服务和消息。

3. 实现服务器

创建 server.go 文件,具体实现 ChatService 服务:

Go 复制代码
package main

import (
	"fmt"
	"io"
	"log"
	"net"

	pb "path/to/your/proto/package"
	"google.golang.org/grpc"
)

type server struct {
	pb.UnimplementedChatServiceServer
}

func (s *server) Chat(stream pb.ChatService_ChatServer) error {
	for {
		in, err := stream.Recv()
		if err == io.EOF {
			return nil
		}
		if err != nil {
			return err
		}
		log.Printf("Received message from %s: %s", in.User, in.Text)

		// Echo message back to client
		err = stream.Send(&pb.ChatMessage{User: "Server", Text: "Echo: " + in.Text})
		if err != nil {
			return err
		}
	}
}

func main() {
	listen, err := net.Listen("tcp", ":50051")
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}

	s := grpc.NewServer()
	pb.RegisterChatServiceServer(s, &server{})

	log.Println("Server is running on port :50051")
	if err := s.Serve(listen); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}
4. 实现客户端

创建 client.go 文件,用于连接服务器并发送和接收消息:

Go 复制代码
package main

import (
	"bufio"
	"context"
	"fmt"
	"log"
	"os"
	"time"

	pb "path/to/your/proto/package"
	"google.golang.org/grpc"
)

func main() {
	conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure(), grpc.WithBlock())
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()

	client := pb.NewChatServiceClient(conn)

	stream, err := client.Chat(context.Background())
	if err != nil {
		log.Fatalf("failed to create stream: %v", err)
	}

	waitc := make(chan struct{})

	go func() {
		reader := bufio.NewReader(os.Stdin)
		for {
			fmt.Print("Enter message: ")
			text, _ := reader.ReadString('\n')

			if err := stream.Send(&pb.ChatMessage{User: "Client", Text: text}); err != nil {
				log.Fatalf("failed to send message: %v", err)
				break
			}
		}
		stream.CloseSend()
	}()

	go func() {
		for {
			in, err := stream.Recv()
			if err == io.EOF {
				close(waitc)
				return
			}
			if err != nil {
				log.Fatalf("failed to receive message: %v", err)
				break
			}
			log.Printf("Received message from %s: %s", in.User, in.Text)
		}
	}()

	<-waitc
}
5. 运行示例

编译并运行服务器和客户端:

  • 编译服务器和客户端:

    Go 复制代码
    go build -o server server.go
    go build -o client client.go
  • 启动服务器:

    Go 复制代码
    ./server
  • 启动客户端:

    Go 复制代码
    ./client
    总结

    在这个例子中,服务器和客户端可以通过双向流(Bidirectional Streaming RPC)方式进行实时通信。客户端通过终端输入消息,并通过 gRPC 发送到服务器,服务器接收到消息后回显给客户端。这个示例展示了 gRPC 强大且灵活的流处理能力,在实际项目中可以根据需求进行扩展和修改。

Go 复制代码
import cycle not allowedgo list
could not import example.com/user/go-project/grpc/service2 (missing metadata for import of "example.com/user/go-project/grpc/service2")

这个错误提示说您在导入路径中循环引用或缺少某些必要的元数据。通常,这些问题可能是因为错误的导入路径配置、循环依赖或者项目结构问题。

步骤 1:确认 go.mod 和目录结构正确

确保 go.mod 文件在项目根目录中,并且包含正确的模块路径和依赖项。

假设项目结构为:

Go 复制代码
/home/user/go-project/grpc/
├── go.mod
├── go.sum
├── service1/
│   ├── chat.proto
│   ├── chat.pb.go
│   ├── chat_grpc.pb.go
│   ├── server.go
│   └── client.go
└── service2/
    ├── chat.proto
    ├── chat.pb.go
    ├── chat_grpc.pb.go
    ├── server.go
    └── client.go

确认 go.mod 文件

./grpc 目录下,初始化并正确配置 go.mod 文件:

Go 复制代码
cd /home/user/go-project/grpc
go mod init example.com/user/go-project/grpc

更新 go.mod 文件以包含必要的依赖,确保内容类似如下:

Go 复制代码
module example.com/user/go-project/grpc

go 1.17

require (
    google.golang.org/grpc v1.39.0
    google.golang.org/protobuf v1.26.0
)

运行以下命令来下载和更新所有依赖:

Go 复制代码
go mod tidy
相关推荐
星恒讯工业路由器2 小时前
5G FWA技术演进与趋势
网络·5g·信息与通信·4g·5g fwa·3gpp规范·fwa趋势
星夜夏空995 小时前
网络编程(1)
服务器·网络
上海云盾-小余5 小时前
中小站点防护避坑:低价高防服务存在的各类安全短板剖析
网络·爬虫·安全·ddos
X7x57 小时前
跨厂商路由引入(重发布)实战指南:华为、华三、思科的逻辑差异与配置避坑
网络协议·信息与通信·信号处理·实施路由引入
数据知道7 小时前
邮件安全实战:SPF/DKIM/DMARC 配置与钓鱼绕过
网络·安全·web安全·网络安全·邮件安全
玩转4G物联网7 小时前
FS800DTU 新品上线|免费使用 DMP 设备管理平台,支持远程批量配置 & OTA 升级
物联网·网络协议·tcp/ip·云平台·核心板·fs800dtu·iot管理平台
王莎莎-MinerU7 小时前
MCP 解决的是工具接入,科研 Agent 还缺的是科学证据接口标准化
开发语言·网络·人工智能·深度学习·pdf·c#·php
Chloeis Syntax8 小时前
JAVAEE初阶 --- 构造HTTP请求
网络·网络协议·http·postman
砚凝霜8 小时前
软考网络工程师|第 1 章 计算机网络基础 完整备考笔记
网络·笔记·计算机网络
rcms152702692188 小时前
NIKON 4S019-394 接口模块
网络