@
sthwrong 突然就没了是指 nbio 删掉了?我说放弃不是这个意思,而是放弃进一步开发或者优化了,以后只做基础的维护。
我总结了一些 nbio 可以优化的点,需要新开个仓库或者 v2 的方式,不与旧版本兼容:
1. 不兼容标准库 http 了,改成纯异步,因为标准库是同步的方式、http handler 退出就意味着这个请求结束,但如果涉及 handler 内逻辑需要阻塞(例例如请求数据中间件、调用 rpc 、请求三房 api ),那么协程就需要常驻等待。
2. 纯异步后,不用等到 http body 收完整就调用 handler ,并且提供异步的 OnBody 给用户使用。现在的方式是读到完整请求(包括大 body )再调用 http handler ,遇到比如上传大文件这种场景内存就扛不住了,所以应该改成异步、读到一点就处理一点、避免内存压力。
3. buffer 的严格一对一分配和释放,这样有利于引入 c 的内存池 lib 例如 jemalloc 、tcmalloc 做内存优化。现在的方式,因为早期没考虑这么细,实现上使用 sync.Pool ,而 sync.Pool 即使不严格一对一 Put 也会在不需要的时候被 gc 、不会导致内存泄漏,所以当前版本不是严格的一对一分配释放,这样的话就没法使用 c 的内存池库优化了、因为会内存泄漏。我倒是想过另一个办法,利用 Finalizer 在变量被 gc 的时候自动去调用释放、避免有遗漏释放的情况,但这种毕竟也是不严格、如果遇到短期洪峰也可能会爆、还不如 runtime gc 压制 OOM 的效果好。
4. protocol stack 的方式去实现协议栈的解析。比如 tcp/udp 收到数据后,给上层的 tls parser 去解析,tls 解析后得到的 buffer 再给 http 或者 websocket 的 parser 去解析。当前不是这种类似中间件、协议栈链的方式去层层传递的,所以支持多层次的协议或者一些扩展时实现起来比较痛苦。例如有些用户想用同一个端口监听 http 、sock5 、普通 tcp 自定义协议,nbio 现在的方式是硬编码、很难去做这种根据第一段 buffer 的内容判断是哪种协议再分流的功能,只能让用户自己实现 listener 然后再额外包装再转给 nbio 之类的,但这种观念方式意味着用户自己实现的 listener 需要为每个链接阻塞读到至少 N 字节的协议头数据、这意味着每个链接要开一个协程,与 nbio 的设计就冲突了。
放弃的原因主要因为:
1. 如果改纯异步,与现在兼容标准库 http 就设计冲突了。
2. 上面提到的几个优化点,虽然实现方式了然于胸,但工作量不小,我现在没这个精力去做。
3. golang 的性能虽然已经不错了,但我对它的性能还是不满意、甚至失望。尤其这种海量连接数的场景毕竟是少数、而且都是基础设施比如网关之类的,这种场景 c/c++/rust 性能和开销更优势,golang 除了开发效率、毫无优势,而能有这种并发量级的通常是大厂、有人力物力去用 c/c++/rust 做,比如 CF 开源的那个 rust 版的 pingora 。