本篇将举例grpc的流模式数据传输
目录
1. 程序结构目录
/rpc_stream
/client
-main.go
/proto
-product.proto
/proto_service
-pruduct.pb.go
/server
-main.go
-pruduct.go
-main.go
2. proto文件
// 使用的语法版本
syntax = "proto3";
// 生成的go文件包
option go_package = "./proto_service";
//service 服务
service ProdService {
rpc GetProductStock(ProductRequest) returns (ProductResponse);
// 客户端流
rpc GetProductStockClientStream(stream ProductRequest) returns (ProductResponse);
// 服务端流
rpc GetProductStockServerStream(ProductRequest) returns (stream ProductResponse);
// 双向流
rpc GetProductStockBothStream(stream ProductRequest) returns (stream ProductResponse);
}
message ProductRequest {
int32 prod_id = 1;
}
message ProductResponse {
int32 prod_stock = 1;
}
生成go文件
pwd
/rpc/proto
protoc --go_out=plugins=grpc:../ product.proto
命令会直接生成proto_service目录和product.pb.go文件
客户端流模式
1. 客户端代码
server/product.go
package main
import (
"context"
"io"
"log"
"rpc_stream/proto_service"
"time"
)
var ProductService = &productService{}
type productService struct {
}
func (p productService) GetProductStock(ctx context.Context, request *proto_service.ProductRequest) (*proto_service.ProductResponse, error) {
log.Println("request: ", request)
return &proto_service.ProductResponse{ProdStock: request.ProdId*1000 + 996}, nil
}
func (p productService) GetProductStockClientStream(stream proto_service.ProdService_GetProductStockClientStreamServer) error {
rsp := &proto_service.ProductResponse{ProdStock: 0}
for {
recv, err := stream.Recv()
if err != nil {
log.Printf("stream.Recv:%v", err)
if err == io.EOF { // 流结束会返回EOF错误
err := stream.SendAndClose(rsp)
if err != nil {
log.Printf("stream.SendAndClose:%v", err)
return err
}
return nil
}
return err
}
log.Printf("服务端接收到流 prodid:%d", recv.ProdId)
rsp.ProdStock += recv.ProdId
}
}
server/main.go
package main
import (
"google.golang.org/grpc"
"log"
"net"
"rpc_stream/proto_service"
)
const addr = "127.0.0.1:8001"
func main() {
// grpc server
rpcServer := grpc.NewServer()
proto_service.RegisterProdServiceServer(rpcServer, ProductService)
listener, err := net.Listen("tcp", addr)
if err != nil {
log.Fatal("启动监听出错", err)
}
err = rpcServer.Serve(listener)
if err != nil {
log.Fatal("启动服务出错", err)
}
}
2.客户端代码
client/main.go
package main
import (
"context"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"log"
"rpc_stream/proto_service"
"time"
)
const addr = "127.0.0.1:8001"
func main() {
conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatal("服务端出错,连接不上:", err)
}
defer conn.Close()
client := proto_service.NewProdServiceClient(conn)
// 客户端流方法调用
stream, err := client.GetProductStockClientStream(context.Background())
if err != nil {
log.Println("GetProductStockClientStream ERR:", err)
}
rsp := make(chan struct{}, 1)
go func(stream proto_service.ProdService_GetProductStockClientStreamClient, rsp chan struct{}) {
for i := 0; i < 11; i++ {
request := &proto_service.ProductRequest{ProdId: int32(i)}
err := stream.Send(request)
if err != nil {
log.Fatalf("stream.Send:%v", err)
}
time.Sleep(time.Second * 1)
}
rsp <- struct{}{}
}(stream, rsp)
select {
case <-rsp:
recv, err := stream.CloseAndRecv()
if err != nil {
log.Fatalf("stream.CloseAndRecv:%v", err)
}
log.Printf("recv:%v", recv.ProdStock)
}
}
服务端流模式
1.服务端代码
server/product.go
func (p productService) GetProductStockServerStream(request *proto_service.ProductRequest,
stream proto_service.ProdService_GetProductStockServerStreamServer) error {
for i := int32(0); i < request.ProdId; i++ {
rsp := &proto_service.ProductResponse{ProdStock: i*1000 + 110}
err := stream.Send(rsp)
if err != nil {
log.Printf("stream.Send:%v", err)
return err
}
time.Sleep(time.Second)
}
return nil
}
2.客户端代码
client/main.go
const addr = "127.0.0.1:8001"
func main() {
conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatal("服务端出错,连接不上:", err)
}
defer conn.Close()
client := proto_service.NewProdServiceClient(conn)
// 服务端流
req := &proto_service.ProductRequest{ProdId: 9}
stream, err := client.GetProductStockServerStream(context.Background(), req)
if err != nil {
log.Fatalf("client.GetProductStockServerStream:%v", err)
}
for {
recv, err := stream.Recv()
if err != nil {
log.Printf("stream.Recv:%v", err)
if err == io.EOF {
log.Println("stream.Recv: EOF end")
}
break
}
log.Println("client recv:", recv.ProdStock)
}
}
双向流模式
1.服务端代码
server/product.go
func (p productService) GetProductStockBothStream(stream proto_service.ProdService_GetProductStockBothStreamServer) error {
for {
recv, err := stream.Recv()
if err != nil {
log.Printf("stream.Recv:%v", err)
return err
}
time.Sleep(time.Second)
log.Printf("服务端接收到流 prodid:%d", recv.ProdId)
rsp := &proto_service.ProductResponse{ProdStock: recv.ProdId + 1}
err = stream.Send(rsp)
if err != nil {
log.Printf("stream.Send:%v", err)
return err
}
}
}
2.客户端代码
client/main.go
const addr = "127.0.0.1:8001"
func main() {
conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatal("服务端出错,连接不上:", err)
}
defer conn.Close()
client := proto_service.NewProdServiceClient(conn)
// 双向流
stream, err := client.GetProductStockBothStream(context.Background())
if err != nil {
log.Fatalf("client.GetProductStockServerStream:%v", err)
}
err = stream.Send(&proto_service.ProductRequest{ProdId: 1})
if err != nil {
log.Fatalf("stream.Send:%v", err)
}
for {
recv, err := stream.Recv()
if err != nil {
log.Fatalf("stream.Recv:%v", err)
}
log.Printf("recv:%v", recv.ProdStock)
time.Sleep(time.Second)
request := &proto_service.ProductRequest{ProdId: recv.ProdStock + 1}
err = stream.Send(request)
if err != nil {
log.Fatalf("stream.Send:%v", err)
}
}
}