vlambda博客
学习文章列表

面对疾风吧!io_uring 优化 nginx 实战演练

文/玖一

引言

io_uring 是 Linux 内核在 v5.1 引入的一套异步 IO 接口,随着其迅速发展,现在的 io_uring 已经远远超过了纯 IO 的范畴。从 Linux v5.3 版本开始,io_uring 陆续添加了网络编程相关的 API,对用户提供 sendmsg、recvmsg、accept、connect 等接口的异步支持,将 io_uring 的生态范围扩大到了网络领域。

另外从 Linux v5.7 开始,io_uring 对这些异步接口提供 FAST POLL 机制,用户无需再使用像 select、event poll 等多路复用机制来监听文件句柄,只要把读写请求直接丢到 io_uring 的 submit queue 中并提交,当文件句柄不可读写时,内核会主动添加 poll handler,当文件句柄可读写时主动调用 poll handler 再次下发读写请求,从而减少系统调用次数提高性能。

上一篇我们初探了 io_uring 用于网络的编程模型以及 echo server benchmark 下的性能表现,这篇文章我们将基于通用应用 nginx 实战。


Nginx io_uring 代码优化

Nginx 是一款轻量级的 Web 服务器、反向代理服务器,由于它的内存占用少,启动极快,高并发能力强,在互联网项目中广泛应用。

从架构上看,Nginx 由一个 master 和多个 worker 进程组成,多个 worker 之间不需要加锁,独立处理与 client 的连接和网络请求。worker 是一个单线程大循环,这与上一篇“你认为 io_uring 只适用于存储 IO?大错特错!”文章中描述的 echo server 模型基本一致。


基于 event poll 的编程模型

event poll 是 Nginx 在 Linux 下的默认事件模型。

面对疾风吧!io_uring 优化 nginx 实战演练

event poll 事件模型把 listen fd 以及新建连接的 sock fd 都注册进 event poll 中,当这些 fd 上有数据可读时,等待在 epoll_wait() 的 worker 进程会被唤醒,调用相应的回调函数进行处理,这里的 recv、writev 请求都为同步请求。


基于io_uring的编程模型

前面提到,io_uring 的 FAST POLL 机制允许数据在未 ready 的情况下就直接下发,不需要再把普通连接的 fd 注册进 event poll。另外这里的读写请求通过 io_uring 异步下发,处理流程大致如下:

面对疾风吧!io_uring 优化 nginx 实战演练

事实上,accept() 也可以采取 FAFST POLL 机制,无需等待 listen_fd 数据可读就直接下发,以减少系统调用次数。但在调试过程中发现这样 accept() 失败概率大大增加,而每次失败的 accept() 都会带来一次无效的 sock 内存申请和释放,这个开销较大,因此依然采用类似 event poll 的方式来侦听 listen fd。后续针对这块可以做一些优化。


测试结果

测试环境

  • 测试机器
    CPU: Intel(R) Xeon(R) CPU E5-2682 v4 @ 2.50GHz  64 逻辑核
    server cmdline 添加:mitigation=on

  • nginx配置

user root;http { access_log off; server { access_log off; // 关闭access log,否则会写日志,影响测试 location / { return 200; // 不读本地文件,直接返回200 } }}
  • benchmark
    使用轻量级HTTP性能测试工具wrk进行压测。

  • 测试命令

长连接 wrk -c $connection -t $thread -d 120 $url短连接 wrk -c $connection -t $thread -H "Connection: Close" -d 120 $url

测试结果

长连接

  • connection=1000,thread=200,测试 server 上不同 worker 数目性能。

面对疾风吧!io_uring 优化 nginx 实战演练

worker 数目在 8 以下时,QPS 有 20% 左右的提升。随着 worker 数目增大,CPU 不成为瓶颈,收益逐渐降低。

  • server 单 worker,测试 client 端不同连接数性能(thread 取默认数 2)。

面对疾风吧!io_uring 优化 nginx 实战演练
面对疾风吧!io_uring 优化 nginx 实战演练

可以看到单 worker 情况下,500 个连接以上,QPS 有 20% 以上的提升。从系统调用数目上看,io uring 的系统调用数基本上在 event poll 的 1/10 以内。


短连接

  • connection=1000,thread=200, 测试 server 上不同 worker 数目性能。

短连接场景,io uring 相对于 event poll 非但没有提升,甚至在某些场景下有 5%~10% 的性能下降。究其原因,除了 io uring 框架本身带来的开销以外,还可能跟 io uring 编程模式下请求批量下发而带来的延迟有关。


总结及下一步工作

从笔者目前的测试来看,io_uring 在网络编程方面的优化更适合长连接场景,在长连接场景下最高有 20% 多的提升。短连接场景还有待优化,主要考虑以下两方面:

  • io uring 本身框架开销的优化,当然这个优化对长连接同样适用。

  • 针对短连接的优化,如针对 accept() 请求,先检查是否有数据可读,避免无效内存申请释放;多个 accept() 一起下发等。

nginx 和 echo server 等优化实践相关内容(包含源代码),我们都已经在 OpenAnolis 社区高性能存储 SIG 开源(openanolis.org)。也欢迎大家积极参与讨论和贡献,一起探索 io_uring 的高性能之路。


如果对内容感兴趣,可以钉钉群搜索【Alibaba Cloud Linux OS 开发者&用户群】入群,一起交流。也欢迎扫码关注「云巅论剑」,一起发掘 Linux 相关内核技术,探索操作系统未来发展方向。

————  end  ————

近期重大活动