golang grpc数据传输的流模式

本篇将举例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)
		}
	}
}

rpc微服务实践

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值