grpc流模式的实例

grpc流模式的实例

grpc中的stream,srteam顾名思义就是一种流,可以源源不断的推送数据,或者服务端和客户端长时间数据交互。

grpc的四种数据流

  1. 简单模式 : 即客户端发起一次请求,服务端响应一个数据; 在proto3中不用指定 stream。
  2. 服务端数据流模式: 这种模式是客户端发起一次请求,服务端返回一段连续的数据流。在proto3中如 rpc ServerMod (RequestData) returns (stream ResponseData);
  3. 客户端数据流模式: 与服务端数据流模式相反,这次是客户端源源不断的向服务端发送数据流,而在发送结束后,由服务端返回一个响应。在proto3中如 rpc ClientMod (stream RequestData) returns (ResponseData);
  4. 双向数据流: 客户端和服务端都可以向对方发送数据流,这个时候双方的数据可以同时互相发送,也就是可以实现实时交互。 在proto3中如 rpc AllMod (stream RequestData) returns (stream ResponseData);

本次实例用的 proto3

syntax = "proto3";

option go_package = "./;pb";

service Mod {
    rpc ServerMod (RequestData) returns (stream ResponseData);      //服务端流模式,拉消息
    rpc ClientMod (stream RequestData) returns (ResponseData);      //客户端流模式,推消息
    rpc AllMod (stream RequestData) returns (stream ResponseData);  //双向流模式,能推能拉
}

message RequestData {
    string data = 1;
}

message ResponseData {
    string data = 1;
}

Server端实现

const port = 8082

type server struct{}

// 服务端流模式,拉消息
func (s *server) ServerMod(req *pb.RequestData, res pb.Mod_ServerModServer) error {
	i := 0
	for {
		i++
		//业务代码
		_ = res.Send(&pb.ResponseData{
			Data: fmt.Sprintf("这是发给%s的数据流", req.Data),
		})
		time.Sleep(time.Second * 1)
		if i > 10 {
			break
		}
	}
	return nil
}

// 客户端流模式,推消息
func (s *server) ClientMod(cliStr pb.Mod_ClientModServer) error {
	for {
		//业务代码
		res, err := cliStr.Recv()
		if err != nil {
			fmt.Println("本次客户端流数据发送完了:",err)
			break
		}
		fmt.Println("客户端发来消息:",res.Data)
	}
	return nil
}

// 双向流模式,能推能拉
func (s *server) AllMod(allStr pb.Mod_AllModServer) error {
	wg:=sync.WaitGroup{}
	wg.Add(2)
	//接受客户端消息的协程
	go func() {
		defer wg.Done()
		for  {
			//业务代码
			res, err := allStr.Recv()
			if err != nil {
				fmt.Println("本次客户端流数据发送完了:",err)
				break
			}
			fmt.Println("收到客户端发来消息:",res.Data)
		}
	}()

	//发送消息给客户端的协程
	go func() {
		defer wg.Done()
		i := 0
		for {
			i++
			//业务代码
			_ = allStr.Send(&pb.ResponseData{
				Data: fmt.Sprintf("这是发给客户端的数据流"),
			})
			time.Sleep(time.Second * 1)
			if i > 10 {
				break
			}
		}
	}()
	wg.Wait()
	return nil
}

// 启动
func start() {
	// 1.实例化server
	g := grpc.NewServer()
	// 2.注册逻辑到server中
	pb.RegisterModServer(g, &server{})
	// 3.启动server
	lis, err := net.Listen("tcp", "127.0.0.1:8082")
	if err != nil {
		panic("监听错误:" + err.Error())
	}
	err = g.Serve(lis)
	if err != nil {
		panic("启动错误:" + err.Error())
	}

}

func main() {
	start()
}

Client端实现

package main

import (
	"context"
	"fmt"
	"go_script/grpc_3/pb"
	"google.golang.org/grpc"
	"sync"
	"time"
)

var rpc pb.ModClient

// 服务端流模式
func serverStreamDemo()  {
	res,err:=rpc.ServerMod(context.Background(),&pb.RequestData{Data: "服务端流模式"})
	if err != nil {
		panic("rpc请求错误:"+err.Error())
	}
	for  {
		data,err:=res.Recv() //
		if err != nil {
			fmt.Println("客户端发送完了:",err)
			return
		}
		fmt.Println("客户端返回数据流值:",data.Data)
	}
}

// 客户端流模式
func clientStreamDemo()  {
	cliStr, err := rpc.ClientMod(context.Background())
	if err != nil {
		panic("rpc请求错误:" + err.Error())
	}
	i := 0
	for {
		i++
		_ = cliStr.Send(&pb.RequestData{
			Data: "客户端流模式",
		})
		time.Sleep(time.Second * 1)
		if i > 10 {
			break
		}
	}
}

// 双向流模式
func clientAndServerStreamDemo()  {
	allStr, _ := rpc.AllMod(context.Background())
	wg := sync.WaitGroup{}
	wg.Add(1)

	//接受服务端消息的协程
	go func() {
		defer wg.Done()
		for {
			//业务代码
			res, err := allStr.Recv()
			if err != nil {
				fmt.Println("本次服务端流数据发送完了:", err)
				break
			}
			fmt.Println("收到服务端发来消息:", res.Data)
		}
	}()

	//发送消息给服务端的协程
	go func() {
		defer wg.Done()
		i := 0
		for {
			i++
			//业务代码
			_ = allStr.Send(&pb.RequestData{
				Data: fmt.Sprintf("这是发给服务端的数据流"),
			})
			time.Sleep(time.Second * 1)
			if i > 10 {
				break
			}
		}
	}()
	wg.Wait()
}

// 启动
func start() {
	conn, err := grpc.Dial("127.0.0.1:8082", grpc.WithInsecure())
	if err != nil {
		panic("rpc连接错误:" + err.Error())
	}
	defer conn.Close()
	rpc = pb.NewModClient(conn) //初始化

	serverStreamDemo() //服务端流模式

	clientStreamDemo()  //客户端流模式

	clientAndServerStreamDemo() // 双向流模式
}

func main() {
	start()
}

最后

可以在实际业务中灵活使用,根据漫哥的经验可以按照需求进行设计数据在各个子服务中的流向;

### gRPC单向流的实现原理 gRPC中的单向流允许客户端发送多个请求到服务器,而服务器只返回一个响应。这种模式非常适合于批量处理场景或者数据收集操作。 在gRPC中定义服务时,可以通过指定`stream`关键字来表示消息流的方向。如果只有客户端发送一系列的消息给服务器,而在最后由服务器返回单一的结果,则这就是所谓的**单向流**[^1]。 #### 定义协议缓冲区文件(`.proto`) 要实现单向流功能,首先需要在`.proto`文件中定义相应的服务接口。下面是一个简单的例子: ```protobuf service StreamService { // 客户端发起多条请求,服务器最终返回一条响应 rpc QueryStream (stream MultiStreamReq) returns (MultiStreamResp); } message MultiStreamReq { string data = 1; } message MultiStreamResp { string result = 1; } ``` 上述代码片段展示了如何通过`stream`关键字标记客户端发出的一系列请求,并让服务器仅返回一次响应[^2]。 #### 编写服务端逻辑 服务端需实现接收来自客户端的数据流并对其进行处理的功能。以下是基于C++的一个简单示例: ```cpp class StreamServiceImpl final : public StreamService::Service { public: grpc::Status QueryStream(grpc::ServerContext* context, const std::unique_ptr<grpc::ServerReader<MultiStreamReq>>& reader, MultiStreamResp* response) override { MultiStreamReq request; std::string aggregated_data; while (reader->Read(&request)) { aggregated_data += request.data(); } response->set_result(aggregated_data); // 将所有接收到的数据拼接成结果 return grpc::Status::OK; } }; ``` 在此段代码里,我们创建了一个继承自`StreamService::Service`的服务类实例,在其中重写了`QueryStream`方法用于读取输入流并将这些数据累加起来作为最终输出的一部分[^3]。 #### 启动gRPC Server 为了使上面编写的业务逻辑生效,还需要初始化并运行gRPC server: ```cpp void RunServer() { std::string server_address("0.0.0.0:50051"); StreamServiceImpl service; grpc::ServerBuilder builder; builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); builder.RegisterService(&service); std::unique_ptr<grpc::Server> server(builder.BuildAndStart()); // 主事件循环保持server处于活动状态直到手动停止 server->Wait(); } ``` 此部分负责配置监听地址、注册具体的服务处理器以及启动整个gRPC框架进入工作状态。 --- ### 示例总结 以上就是关于gRPC单向流的基本概念及其实际应用方式介绍。它主要适用于那些希望一次性获取全部计算成果的应用场合下非常有用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值