grpc之AsyncNext:获取完全队列中的请求/响应
grpc异步传输,请求,应答都依赖于完全队列CompletionQueue(客户端),ServerCompletionQueue(服务器);客户端,服务器使用Next/AsyncNext方法获取响应,如下:
void * tag - nullptr;
bool ok = false;
CompletionQueue cq;
if(cq.Next(&tag, &ok))
{
//逻辑实现
//客户端处理回应
//服务器处理请求
}
如上代码,Next,AsyncNext如果由响应返回true。下面详细介绍下这两个函数:
Next
获取队列中可用的请求/响应,如果没有可用的数据则程序阻塞,直到获取到可用数据。
bool Next(void** tag, bool* ok)
/*
描述:
获取队列中可用的请求/响应
参数:
tag: 请求/响应的对象指针
ok: rcp的连接状态,连接异常返回false
*/
AsyncNext
获取队列中可用的请求/响应,如果没有可用的数据则阻塞一定时间然后返回,这个阻塞的时间就是设置的超时时间。
template <typename T>
NextStatus AsyncNext(void** tag, bool* ok, const T& deadline)
/*
描述:
获取队列中可用的请求/响应
参数:
tag: 请求/响应的对象指针
ok: rcp的连接状态,连接异常返回false
deadline:超时
*/
用法
Next的用法不多做解释,参考概要中的示例,下面着重介绍下AsyncNext的用法,个人认为AsyncNext比Next更加好用;废话不多说直接上代码方便理解:
void thread_pro(void)
{
void *got_tag;
bool ok = false;
int i = 0;
//设置超时时间
gpr_timespec deadline = gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_millis(1000, GPR_TIMESPAN));
// while (cq_.Next(&got_tag, &ok))
while (cq_.AsyncNext(&got_tag, &ok, deadline))
{
InterfaceAsyncClientCall *call = static_cast<InterfaceAsyncClientCall *>(got_tag);
if (call != nullptr)
{
if (!ok)
{
got_tag = nullptr;
ok = false;
deadline = gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_millis(1000, GPR_TIMESPAN));
continue;
call->status = Status::OK; // 事件异常断开,强行终止。
}
call->Proceed();
}
got_tag = nullptr;
ok = false;
//更新超时时间的时间值
deadline = gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_millis(1000, GPR_TIMESPAN));
}
}
该代码是我实际项目中用到的,着重说以超时机制,该机制和我们的普遍的认知还是有点区别的:
设置超时时间
gpr_timespec deadline = gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_millis(1000, GPR_TIMESPAN));
gpr_time_add:将两个时间值相加,某时刻的时间值+时间间隔,第一个参数是时间值,第二个参数是时间间隔。
gpr_now(GPR_CLOCK_REALTIME):获取当前时间;
gpr_time_from_millis(1000, GPR_TIMESPAN):设置时间间隔为1s。
假设时间值为2023-07-13:00:00:00, 时间间隔为1s,则返回值deadline的值为2023-07-13:00:00:01
所以这行代码:
while (cq_.AsyncNext(&got_tag, &ok, deadline))
如果完全队列没有响应,则AsyncNext方法阻塞到2023-07-13:00:00:01时刻进行返回,所以AsyncNext方法的超时是以某时刻为准,下次循环当前时间已经超过deadline的时间值从而没有超时效果;
所以如果想要实现每次循环都阻塞1s需要在每次循环结束之前重新更新超时时间的时间值,示例代码的27行就起到更新作用。
值得注意的一点是:
got_tag = nullptr;
ok = false;
这两个局部变量也要实时更新,因为got_tag,ok变量在while外部定义,但是在队列中没有请求/响应的时候got_tag的状态不会改变,举个例子:
程序运行后还没有任何请求,则got_tag为nullptr,这个很正常表示队列中没有数据,假如来了一个请求/响应,got_tag为请求/响应的对象的指针;但是如果之后没有请求/响应(队列中没有可用数据)got_tag仍然指向上一个请求对象的地址。这就导致没有请求也会进入if (call != nullptr)容错中。
因此我一般会在每次循环结束就将got_tag的状态重新指向nullptr,方便以后的判断和操作。
想要了解更多grcp的技术用法及音视频相关技术请参考我的主页博客获取更多,或者持续关注我的作品。
- 点赞
- 收藏
- 关注作者
评论(0)