gRPC 是一个支持多种语言的高性能 RPC 框架,拥有丰富的 API 来简化服务端和客户端的开发过程。gRPC 支持四种 RPC 类型:Unary RPC、Server Streaming RPC、Client Streaming RPC 和 Bidirectional Streaming RPC。下面是双向流 API 的使用方法。
双向流 API 示例
在双向流中,客户端和服务器之间可以同时发送和接收消息。以下是一个双向流的完整示例,展示了客户端和服务端之间的聊天功能。
1. 定义 Protobuf 文件
首先,定义一个 Protobuf 文件 chat.proto
,描述服务和消息结构:
syntax = "proto3";package chat;service ChatService {// 双向流 RPCrpc Chat(stream ChatMessage) returns (stream ChatMessage);
}message ChatMessage {string user = 1;string text = 2;
}
2. 编译 Protobuf 文件
使用 protoc
编译器生成 Go 代码:
protoc --go_out=paths=source_relative:. --go-grpc_out=paths=source_relative:. chat.proto
这会生成 chat.pb.go
文件,其中包含了定义的服务和消息。
3. 实现服务器
创建 server.go
文件,具体实现 ChatService
服务:
package mainimport ("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 clienterr = 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
文件,用于连接服务器并发送和接收消息:
package mainimport ("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 build -o server server.go go build -o client client.go
- 启动服务器:
./server
- 启动客户端:
./client
总结
在这个例子中,服务器和客户端可以通过双向流(Bidirectional Streaming RPC)方式进行实时通信。客户端通过终端输入消息,并通过 gRPC 发送到服务器,服务器接收到消息后回显给客户端。这个示例展示了 gRPC 强大且灵活的流处理能力,在实际项目中可以根据需求进行扩展和修改。
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
文件在项目根目录中,并且包含正确的模块路径和依赖项。
假设项目结构为:
/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
文件:
cd /home/user/go-project/grpc
go mod init example.com/user/go-project/grpc
更新 go.mod
文件以包含必要的依赖,确保内容类似如下:
module example.com/user/go-project/grpcgo 1.17require (google.golang.org/grpc v1.39.0google.golang.org/protobuf v1.26.0
)
运行以下命令来下载和更新所有依赖:
go mod tidy