Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

后端实时推送数据到客户端的问题 #210

Closed
guochaopeng opened this issue Apr 27, 2022 · 21 comments
Closed

后端实时推送数据到客户端的问题 #210

guochaopeng opened this issue Apr 27, 2022 · 21 comments

Comments

@guochaopeng
Copy link

如果我要从后端实时推送数据到客户端,应用workflow的rpc框架,要怎么应用来做到实时推送呢

@holmes1412
Copy link
Contributor

由于目前的rpc都是一来一回模式,因此想实现server主动推送到client,有一个最简单的方法是使用watch_timeout,这是对方第一次回复的超时,用法直接client.set_watch_timeout(60 * 1000 /* 60秒*/),网络模型大概就是:

client发起task -------watch------> server干别的

client收到消息 <--------推送------ server发起推送
client再发起task-------watch------> server可以先干别的

client收到消息 <--------推送------ server发起推送

@Barenboim
Copy link
Contributor

你可以在client端也开个srpc server的。连上server之后,把自己的ip port发过去。然后server端就可以rpc client啦。

@guochaopeng
Copy link
Author

由于目前的rpc都是一来一回模式,因此想实现server主动推送到client,有一个最简单的方法是使用watch_timeout,这是对方第一次回复的超时,用法直接client.set_watch_timeout(60 * 1000 /* 60秒*/),网络模型大概就是:

client发起task -------watch------> server干别的

client收到消息 <--------推送------ server发起推送 client再发起task-------watch------> server可以先干别的

client收到消息 <--------推送------ server发起推送

那这样就是要前端定时去请求,是吗

@holmes1412
Copy link
Contributor

@guochaopeng 不是定期,是保持自己watch了就可以了。比如下次收到了推送,那你再watch一次。
当然我觉得楼上说的前端也悄悄开个server,让后端连上去,就可以被后端主动推送了,这个方案也挺好的。

@guochaopeng
Copy link
Author

可以弄个案例吗?没有太理解

@guochaopeng
Copy link
Author

@guochaopeng 不是定期,是保持自己watch了就可以了。比如下次收到了推送,那你再watch一次。 当然我觉得楼上说的前端也悄悄开个server,让后端连上去,就可以被后端主动推送了,这个方案也挺好的。

可以弄个案例吗?没有太理解

@Barenboim
Copy link
Contributor

Barenboim commented Apr 29, 2022

@guochaopeng 不是定期,是保持自己watch了就可以了。比如下次收到了推送,那你再watch一次。 当然我觉得楼上说的前端也悄悄开个server,让后端连上去,就可以被后端主动推送了,这个方案也挺好的。

可以弄个案例吗?没有太理解

你好。前面关于watch的解释有一点小问题。
其实watch并不是一个特殊的命令,而只是一个超时设置。watch_timeout的定义是,client的每一个请求发出之后,收到server返回的第一个数据包最长等多久的意思。这个参数可以在client的构造时指定:

{
    struct RPCClientParams params = RPC_CLIENT_PARAMS_DEFAULT;
    params.task_params.watch_timeout = 30 * 1000; // 30 seconds
    params.host = "127.0.0.1";
    params.port = 1412;
    Example::SRPCClient client(&params);
    ...
}

通过这个方法,所有rpc调用,都可以有一个watch时间了。而receive_timeout会在watch到第一个数据包之后,才开始计算。
另外,你也可以单独给每个client task设置watch timeout。task级的timeout显然会比client级的更加优先。

@Barenboim
Copy link
Contributor

其实,如果是server的主动推送,我们更推荐client端再开一个rpc server的方案。这个方案在我们公司内部大量应用,我认为是收缩rpc调用模型的一个好方法。

@guochaopeng
Copy link
Author

其实,如果是server的主动推送,我们更推荐client端再开一个rpc server的方案。这个方案在我们公司内部大量应用,我认为是收缩rpc调用模型的一个好方法。

如果我的客户端不是使用的C端,而是使用的B端请求呢?

@Barenboim
Copy link
Contributor

其实,如果是server的主动推送,我们更推荐client端再开一个rpc server的方案。这个方案在我们公司内部大量应用,我认为是收缩rpc调用模型的一个好方法。

如果我的客户端不是使用的C端,而是使用的B端请求呢?

浏览器的话,只能在一个连接上让server不停的push了。这个workflow里是支持的,但push接口我们没有在srpc项目里实现。
其实如果你是纯http,用workflow就可以了,不一定要加入rpc。

@lesismal
Copy link

lesismal commented May 13, 2022

可以考虑支持notify的功能,就是一方只发数据通知对方、不需要响应:

Pattern Interactive Directions Description
call two-way:
c -> s
s -> c
request and response
notify two-way:
c -> s
s -> c
request without response

@holmes1412
Copy link
Contributor

@lesismal 感谢QWQ 这个表格很清晰~
目前主要是srpc的核心通信器是一来一回模式,所以notify这种本质上是个双工通信其实还不太好做。不过我们有分支在尝试支持websocket这样的双工client,最近也改进了一个package功能就是可以分批收数据了,相信notify以后也会有的>_<

@Barenboim
Copy link
Contributor

@lesismal 感谢QWQ 这个表格很清晰~ 目前主要是srpc的核心通信器是一来一回模式,所以notify这种本质上是个双工通信其实还不太好做。不过我们有分支在尝试支持websocket这样的双工client,最近也改进了一个package功能就是可以分批收数据了,相信notify以后也会有的>_<

这里跟传输载体的协议的单双工关系不大,恰当点说rpc的传输载体协议,比如tcp/websocket/kcp或者其他4-7层实现的可靠传输协议,其实基本都是双工的(代表同一个Conn的通道同时既能收也能发)。

而一来一回、一来无回,都是指在传输载体协议之上的一组行为组合或者一个行为定义,比如一发一收叫做Call,一发不需要回复可以叫Notify。 我的框架里,也是支持多种协议作为传输载体协议的,支持所有tcp/tls/websocket/kcp/utp或者其他的实现了golang的net.Listener/net.Conn的协议。

srpc里,如果支持server的Notify,只要支持server主动给client发送一个完整的rpc message就可以了,发出去、不需要等待clietn的回包,发完就完事了,应该不是难事。

这个确实有很多方法。也有用户帮我们魔改了完整的websocket client/server ,这个就是全双工。

@lesismal
Copy link

也有用户帮我们魔改了完整的websocket client/server ,这个就是全双工。

tcp和websocket在作为rpc的传输载体协议时,没有本质区别。网络框架能支持并发call的,就能支持notify,跟传输载体协议的双工没什么直接关系,因为能支持并发call就意味着载体协议是双工,而且tcp和websocket也都是双工的。

@Barenboim
Copy link
Contributor

也有用户帮我们魔改了完整的websocket client/server ,这个就是全双工。

tcp和websocket在作为rpc的传输载体协议时,没有本质区别。网络框架能支持并发call的,就能支持notify,跟传输载体协议的双工没什么直接关系,因为能支持并发call就意味着载体协议是双工,而且tcp和websocket也都是双工的。

我们内部用得更多的,是自己也拍一个server下去。然后把自己的ip:port通过rpc告诉notify server。notify server通过正常的rpc访问client。其实我非常喜欢这种方式,很好的收缩了访问模型。

@holmes1412
Copy link
Contributor

holmes1412 commented May 13, 2022

其实这个确实不是协议上的问题,是与核心通信器的默认模式相关。
我有写过一篇关于Workflow内部的网络优化点:《C++ Workflow异步调度框架 - 性能优化网络篇》,感兴趣可以看看~
Workflow的网络核心之所以可以这么快,核心通信器对连接的高效管理功不可没,所以默认用这种方式可以解决公司内大部分的问题。但正如你说的,开源了需求就多样化了起来,所以我们在尝试多种能同时完美兼容默认高效模式的方法去支持~

@Barenboim
Copy link
Contributor

Barenboim commented May 13, 2022 via email

@Barenboim
Copy link
Contributor

目前的超时算法利用了链表+红黑树的数据结构,时间复杂度在O(1)和O(logn)之间,其中n为网络线程的当前管理的fd数量。
超时处理目前看不是瓶颈所在,因为Linux内核epoll相关调用也是O(logn)时间复杂度,我们把超时都做到O(1)也区别不大。

堆应该是定时器领域更好一点的选择(因为空间亲和性、操作数应该是要好于红黑树的),不过nginx用了红黑树,libevent哪个版本来着好像是优化成了堆。整体性能差异倒是不大,都够用。

这里的O(1)我没有太理解,通常只有时间轮能做到O(1),但是时间轮不精确。另外就是 其中n为网络线程的当前管理的fd数量,按理说,定时器的数量应该是大于fd数量的,因为同一个fd上可能有多个定时器,比如Call的超时可能小于读超时,所以此时可能存在两个超时,还可能有写超时。当然,同一个fd上的定时器数量可能不多,所以用链表管理同一个fd上的几个,也是相当于O(1),这样确实是能做到某些操作设置定时器时,如果超时时间大于这个fd上已有的定时器超时时间,则只需要挂到链表后面就行、不需要再真正去设置到timerfd里,不知道我这样理解对不对

首先,我们一个fd上最多一个超时(也可无超时)。一个fd同时需要关注读写的话,必须通过dup操作多产生一个fd放进去。
另外,我们的数据结构是rbtree+list。因为超时时间总是倾向于增大(绝对时间),所以会先对比list最后一个node的超时,如果比这个大就放在list,否则放在tree。放tree的时候,也优先对比tree的最大节点。
整体下来,大多数操作都是O(1)。

@lyla105
Copy link

lyla105 commented May 15, 2024

由于目前的rpc都是一来一回模式,因此想实现server主动推送到client,有一个最简单的方法是使用watch_timeout,这是对方第一次回复的超时,用法直接client.set_watch_timeout(60 * 1000 /* 60秒*/),网络模型大概就是:

client发起task -------watch------> server干别的

client收到消息 <--------推送------ server发起推送 client再发起task-------watch------> server可以先干别的

client收到消息 <--------推送------ server发起推送

我想请教下,这个srpc跟grpc的区别?
这里说srpc的rpc是一来一回的模式,是对应的grpc的简单模式么?
grpc的流模式,是可以实现服务端的主动推送么?srpc没有对应的流模式的实现,是么?

@Barenboim
Copy link
Contributor

嗯嗯,我们没有主动推送的功能啊。workflow里也只支持到SSE模式。

想做推送可以两边都开server,互相调用,actor模式。我们本质上就是actor。

@lyla105
Copy link

lyla105 commented May 15, 2024

嗯嗯,我们没有主动推送的功能啊。workflow里也只支持到SSE模式。

想做推送可以两边都开server,互相调用,actor模式。我们本质上就是actor。

好的,谢谢~

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants