cloudwego/netpoll

压测建连很慢,listener 跟 accept 得到的 fd 可能分配到相同的 epoll fd 进行管理?

Closed this issue · 3 comments

现象,压测客户端,10000连接,每建立一个连接后就立刻发送数据,10000个连接全部建连成功需要等待很长时间,或者等待很久仍然无法全部建立,netstat 发现大量出于 SYN_SENT 状态

netpoll代码wrap层次稍微有点多,我暂时没review太多。

盲猜一下,listener和client的fd是有可能在同一个epoll loop里吧?如果是这样,当client的fd很忙时,listener accept的响应能力就变慢了,我用来测试的client是每连接上一个,就直接开始数据收发了,如果是listener可能跟accept得到的client fd在同一个epoll loop,现象就能解释通了

如果是这个问题,listener改用单独的epoll loop比较好,accept不是大量频繁操作,所以多个listener放同一个epoll loop也ok,但是最好不要跟 client fd 放到相同的 epoll loop

是的,listener fd 和 client fd 是共享 epoll 的。
单独为 listener fd 分配 epoll 不见得会快很多,因为它只能独占一个 goroutine 而不是独占一个线程。而 listener 独占 epoll 是有一定性能损失的。
事实上,从未见过除压测场景外,有短时间要求建立 10000 连接的真实场景。Netpoll 不是面向压测优化的项目,我们在权衡后认为共享 epoll 在真实场景是更优的。也许未来有一天我们会为 listener 单独分配 epoll,当我们看到线上真的存在 accept 问题的时候。

是的,listener fd 和 client fd 是共享 epoll 的。
单独为 listener fd 分配 epoll 不见得会快很多,因为它只能独占一个 goroutine 而不是独占一个线程。而 listener 独占 epoll 是有一定性能损失的。
事实上,从未见过除压测场景外,有短时间要求建立 10000 连接的真实场景。Netpoll 不是面向压测优化的项目,我们在权衡后认为共享 epoll 在真实场景是更优的。也许未来有一天我们会为 listener 单独分配 epoll,当我们看到线上真的存在 accept 问题的时候。

单独为 listener fd 分配 epoll 不见得会快很多

listener 单独 epoll 更能保证新连接成功的速度,至少其他几个库用同样的 client 压测能够较快成功建立 10000 或者更多连接。

因为它只能独占一个 goroutine 而不是独占一个线程。而 listener 独占 epoll 是有一定性能损失的。

如果想占单独的线程,可以为这个 listener epoll 使用 runtime.LockThread

至于性能损失,如果没有新连接进来时,这个协程/线程也只是等待状态,而且只需要这一个协程/线程就够了,对调度的性能损失基本可以忽略不计

事实上,从未见过除压测场景外,有短时间要求建立 10000 连接的真实场景

短时间大量连接请求是有的,比如非常火爆的游戏开服,开服瞬间1s内几千上万的建连请求过来。

另外,比如 netty,随便搜一下,大家使用 netty 也主要是用这种单独 acceptor 的方式:

EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
bossGroup负责处理客户端的连接请求,workerGroup负责处理I/O相关的操作,执行系统Task、定时任务Task等