前言
我以前有写过一个练手玩具,参考 tinywebserver
当然那时候我稍微改了下,改成了主从 Reactor 和 epoll
不过那时候感觉最后实现的有线程问题,查了半天没查出来红温了,索性加了一把大锁
最近读了下 Sender 模型,正好之前也写了个 coroutine primitive,于是拿来练手
参考资料
虽然 C++26 上通过了 execution,但是参考资料还是非常非常少
由于我还是想从底层开始,所以不考虑网络库(不如说用网络库就没什么工作量了),直接从标准的 stdexec 开始
阅读前,需要先对网络编程,异步,IO,以及最好对协程有一些了解
提案就不放了,太长了我也没仔细看
参考之一:execution 的设计
参考之二:execution 的构造例(这是系列最后一篇)
参考之三:重点,我写的也是基于这个封装(另外,这个作者也是上面那个翻译者,有点巧了)
上述参考中有一些引用,建议都先看一看,毕竟参考资料太少了
gpt 和 claude 的支持都不怎么样,这个时候建议直接把觉得不错的参考代码喂给 ai
由于终版的 P2300 提案中已经去掉了 tag_invoke,改成了成员函数,读源码时需要适当注意下,目前 stdexec 中应该是两种都有?
关于io_uring,看起来虽然只有三个 syscall,但是结合多线程会出现非常多神秘问题,所以就不用裸的,统一用liburing
改写目标
基本上我们要改写的部分是:threadpool 和 HTTP connection,分别对应两个部分
但是由于 execution 提出了非常神的 context 这一概念,我们实际上需要首先写个 scheduler,然后基于这个去写 sender
异步IO的部分,原则上也是需要包在 sender 里的,所以又回来了
我之前读各种资料和源码,读了差不多5天,一直下不去手,后面读到那篇 blog 才好起来
一些注意的地方
首先需要熟悉 sender 这一套方法,sender factory {| sender adaptor} | sender consumer
sender factory 的话,基本上就是 just 或者 schedule(scheduler) 起手
中间是若干个处理,注意执行流程其实就是里面的 lambda,写起来和同步是一样的,基本上就是 let_value 或者 then
最后的 consumer,理论上全局只要写一个 sync_wait 就行了
如果需要循环可以直接 return false 然后接 repeat_effect_until
然而如果报错了,就有早年模板元报错的美了,还加上了 lambda closure 的美,不过总之现代报错都有行标和颜色,只读 error 读起来也能读
实现
关于服务器本身倒没什么好讲的,毕竟 parser 之类的代码都能复用
我还稍微改了下写路径,因为都是静态资源,所以可以直接走 sendfile
这里花了一天多一点
先是写了个基于线程池的版本,然后想一想我都写 sender 了怎么还在显式线程池,这不对吧
于是改了下,改成每个线程有独立的 io_uring 示例,约等于重写,花了两天
然后发现 Ctrl+C 停不下来,于是仔细研读那篇 blog,又适当的重写了一部分才修好
到这里花了两天,基本功能都有了
压测发现到10k并发的时候会出现大量的失败,加了个最大连接数限制后好一点但不多,懒得修了
然后准备加上 timer 和 keep-alive,又花了一天,
之后准备实现