grpc实现c++异步非阻塞stream

本文介绍了如何在grpc中实现C++的异步非阻塞stream服务。作者指出,虽然grpc的异步并非真正的异步,仍会涉及后台线程,但通过completionqueue和tag机制,可以构建出异步处理模型。文章详细讲解了异步模型的工作原理,并提供了一个使用std::function作为tag的示例,展示了如何处理新连接、读写事件和断开连接。代码示例包括服务器和客户端的实现。

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

grpc实现c++异步非阻塞stream

参考文章

  1. Non-blocking single-threaded streaming C++ server
  2. gRPC C++ async api doc and sample code
  3. grpc异步stream server端demo

序言

  原来一直是用着同步阻塞的grpc stream。由于不想再创建新的线程来监听grpc stream的新消息了,所以就想着采用异步非阻塞的方式来实现grpc的stream。但是google了一下,发现关于grpc异步非阻塞的文章很少,大部分都是grpc官方的阻塞的demo,所以特此写一篇文章来介绍grpc异步非阻塞的逻辑和实现的方法。
  首先先给出一个结论,grpc stream采用的模型跟普通的异步非阻塞模型类似,所以逻辑相对复杂,如果没有较好的抽象思维以及一定的编码能力,最好还是采用同步阻塞的方式接收消息。
  如参考文章1里面所说的,grpc的异步并不是真正的异步,仍然会有后台线程:

  1. Note grpc library needs to use the thread donated via Next or AsyncNext to do some background work and thus only AsyncNext infrequently with very short deadline may not be a good idea.
    ( 注意grpc库需要使用通过Next或AsyncNext来做一些后台工作,因此使用一个到期期限很短的AsyncNext可能不是一个好主意 )
  2. Also, regarding the single-threadedness, the current grpc implementation creates internal threads to do background work such as timer handling and others. As a result, you will not have a truly single-threaded server even if you only use one thread for the server.
    ( 此外,关于单线程,当前的grpc的实现会创建了内部线程来完成后台工作,如计时器处理等。因此,即使只为服务器使用一个线程,你也不会有真正的单线程服务器。)

grpc的异步模型

  grpc的异步模型以completion queue和tag为核心,completion queue中存放的是触发的事件对应tag,通过completion queue的AsyncNext函数或者Next函数来取出tag,用户再根据tag去调用具体的事件的自定义处理函数,可以看出tag就是识别触发事件(如新链接接入,收到新消息等)的唯一标识符。tag在grpc中是由用户自己去绑定,它的类型是void*,即无类型指针,相对于某些框架来说,grpc的异步框架更自由一点。框架如下图所示:
在这里插入图片描述

  在grpc中使用双向stream流,一个链接就是一个stream,那么,每当我们在completion queue中取到一个tag,首先,我们要通过tag去判断是哪个stream触发了事件,然后我们要根据tag去判断,到底是什么事件触发了,常用的事件有新链接接入(connect),收到新信息(read),消息发送成功(send),链接断开(disconnect)。tag可以使用一个id,这个id对应一个stream,同时对应一个具体的事件(如收到新信息)。为了方便本人所写的例子中,使用的是 std::function<void(bool)>作为tag,就是绑定了对象的类成员函数指针,该类成员函数为void xxx(bool) ;由于该函数指针绑定了对象,而对象和stream是绑定的, 所以很容易从tag中知道是哪个stream,然后不同的事件绑定不同的类成员函数,通过类成员函数的不同就可以实现不同事件的处理。
在这里插入图片描述

  注意,在stream断开的时候,会触发读完成事件(read)和写完成事件(send),通过completion queue的AsyncNext函数或者Next函数返回的ok标识符就可以区分是正常的读写完成事件,还是因为stream断开触发的。

code下载地址

grpc async stream server
https://gitee.com/evilskyman/grpc-demo.git
grpc async stream client
https://gitee.com/evilskyman/grpc-async-client-stream-demo.git

server code

#include <algorithm>
#include <chrono>
#include <cmath>
#include <cstddef>
#include <iostream>
#include <memory>
#include <string>
#include <thread>
#include <queue>
#include <unordered_set>
#include <mutex>
#include <time.h>

#include <grpc/grpc.h>
#include <grpcpp/security/server_credentials.h>
#include <grpcpp/server.h>
#include <grpcpp/server_builder.h>
#include <grpcpp/server_context.h>

#include "hello.pb.h"
#include "hello.grpc.pb.h"

using grpc::Server;
using grpc::ServerAsyncResponseWriter;
using grpc::ServerBuilder;
using grpc::ServerCompletionQueue;
using grpc::ServerContext;
using grpc::ServerReader;
using grpc::ServerReaderWriter;
using grpc::ServerAsyncReaderWriter;
using grpc::ServerWriter;
using grpc::Status;

using hello::HelloService;
using hello::HelloMsg;
using TagType = std::function<void(bool)>;//本人使用的tag是函数指针,函数指针使用的是绑定了类的类对象函数.

using namespace std;

class GrpcStreamServerInstance;
const static std::string server_address("0.0.0.0:8860");
static unordered_set<GrpcStreamServerInstance *> GrpcStreamServerInstanceSet;//存放着所有的已经连接上的stream对应的GrpcStreamServerInstance
class GrpcStreamServerInterface {
   
   
public:
    virtual void connected(bool ok) = 0; //新连接接入服务器
    virtual void readDone(bool ok) = 0;  //读到一帧新消息
    virtual void writeDone

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值