编辑: ACcyL | 2015-03-30 |
1024 个文件描述符, windows 上的 select 更是劣质到只能使用64个. 就算通过修改定义强迫接受一万个文件描述符, 也没有解决 实际的问题. select 实在是太慢了. 在这种背景下, IBM 老大哥带领着MS老弟先搞了 IOCP . 然而开源的人有开源的做法, 在 NIH 综合症的影响下, BSD 的人敢为天下所不齿, 发明了 Kqueue. 同样在 NIH 综合症影响下, Linux 的一群 M* 的猴子捣鼓出了 epoll. 那些年的网络库
3 分裂, 让人头疼. 然而, 他们都声称自己的新接口对 select 有质的提升, 是破解 c10k 问题的不二法宝. 你用也得 用, 不用也得用. 为了让自己编写的网络程序能跨平台, 程序员开始了对3大各自为阵的法宝的 膜拜学习. 除了需要应对多套互不兼容的 API , 异步本身也需要更高级的抽象, 把程序员从编写异步代码 的地狱模式里拯救出来. 于是程序员们急需一个上天入地无所不能的法宝的法宝, 把这3家法宝 给统御起来. 率先站出来瑟的是 ACE. 瑟的 ACE 恰乱世刚过, 天下待定, C++ 委员会的老人们却韬光养晦, 不问世事. 所谓乱世出英雄, 英雄出 少年, 欧文大学出了名秀才. 凭借其洋洋洒洒的一片雄文 《Pattern-Oriented Software Architecture》 中举去了首府学城, 并为ACE奠定了无可撼动的地位. ACE 的名字, 也许灵感来自 Adaptive Clubbed Rod, 这也是当年一位英雄少年的宝贝. 既是宝 贝, 必需如意. 即是后来的葫芦娃都怕了 如意宝贝 . ACE 如意在什么地方呢?如意其一, 支持 IOCP/kqueu/epoll/select/you_name_it 各种接口, 号 曰没有不能跨的平台. 如意其二, 支持多种模型.这些模型都在《Pattern-Oriented Software Architecture》有过详细叙述. ACE 本身就是这篇论文的实践,因为他知道, 纸上得来终觉浅 绝知此事要躬行. 如意其三, 接口和模式排列组合下, 多少种, 竟可不修改代码而适应. 然而 ACE 毕竟嫩了点, 没过几年就失势了. 现在除了一些老程序员还在用, 新生代的程序员已 经不再使用 ACE 了. 为什么呢? 陈硕在他的博客里说, ACE 过于复杂,甚至比它试图封装的 对象更复杂, 程序员是指望用你的如意宝贝去驾驭另外那三家宝贝的, 结果你比他们还难. ACE 犯了早期 C++ 库都会犯的一个错误,过度设计, 过度java化.所谓 java 化, 就是以对 象代替接口, 以虚函数代替回调,以继承代替组合.以虚类代替模板.对象间关系错综复 杂,牵一发而动全身.除了作者,已经无人能参与 ACE 的开发了. 与此同时,C语言的回归却在背后悄然进行.C语言的复辟,带来了几个更为糟糕的替代品, libevent 和 libev,以及 乘着nodejs的盛行东风而来的 libuv. 原始的 libevent C语言有着顽强的生命力,当然,这并不是因为C语言有多好,在后续的章节里我们还会深入 的探讨C++相对C的改进.C语言的顽强和人天生的懒惰和偏见是有一定关系的.这种惰性表 现为随遇而安,表现为固执己见. 非要拼命的否定C++,固守 C , 对 C 的缺点视而不见,诋毁C++相对C的改进.固守的结果就是简陋原始的 libevent . 然而因为保守党巨大的人数优 势, libevent 应其群众基础良好而获得了空前的广泛使用. 那些年的网络库
4 libevent 就如名字所言,是一个异步事件框架.从 OS 那里获得事件, 然后派发.派发机制 就是 回调函数 .异步异步,归根结底就是处理从操作系统获得的事件.iocp也好, epoll也罢,都只是用来获取事件的接口.libevent 去掉了ACE华而不实的包装,保留了异步事件,极 大的简化了模型.不得不说软件工程是个糟糕的发明,从来都把简单问题复杂化.libevent把 简单问题简单化,让异步网络编程反朴归真,应该来说,本是一个好库. 然而 libevent 因为设计缺陷,例如使用全局变量,定时器无法处理时间跳变,诸如此类的设 计缺陷导致了 libev 的出现. libev 就是为了克服libevent的缺陷而诞生的.然而,libev 就一 定好了吗? 禁锢的 libev libev 带着对 libevent 的怨气出世了. 吸收了 libevent 的所有缺点, 虽然承诺过改进.然而 libev 如何改进的了呢? libev 已经够原始了,向下改进还不如让人直接使用系统的 api, 向上 改进,一是会导致和libevent的重叠,二是很快就碰到了 C 语言强加的禁锢. C 语言因其语法简陋简洁而著称.然而,缺乏必要的抽象能力,导致 C 语言编写异步程序, 就如同安迪拿着小锤子琢开肖生克监狱的墙壁一样.能,但是要耗费巨大的精力和时间. 编 写异步程序, 最需要的2个抽象能力, 其一为协程,其二是函数对象,包括匿名函数对象, 也就是lambda. C统统没有.函数对象是实现闭包比不可少的,如果没有函数对象, 就只能 通过携带 void* 指针的形式迂回完整,繁琐不说,还特别容易出错.程序里也到处充满了类 型强转.到处是临时定义的类型,就为了传给 void* 使用. 尽管C 有那么多缺点,然而 libev 还未来得及被C的缺点拖累,因为他不支持 IOCP. 于是 libuv 就出来给 libev 擦屁股了. 支持了 iocp 后的 libuv 就真的只有 C 本身的缺点了吗? 混乱的 libuv libuv 可以说是 C 语言的异步库所能达到的最高高度了.完完全全的触碰到了C语言的自身瓶 颈,好在 libuv 只是 nodejs 的底层库,上层软件转移到 javascript 语言而逃避了 C 的禁锢. 真的是这样的吗? libuv 自身还有什么缺点呢? 开源社区avplayer的大拿jackarain曾经说过,一个网络库好不好,就看他有没有正确的处理 TCP 关闭, read write 实现的对不对. libuv 很遗憾的是,不合格.libuv 的 uv_write 没有返 回值,允许空回调.也就是忽略write错误. 网络出错的情况下, libuv 的用户只能稀里糊涂 的知道出错了, 至于错在哪?数据到底有没有发出去了? 一概不知道. 把数据交给 uv_write 后,就是一笔糊涂账了,大概 TCP 不可靠的说法就是从这里传出来的吧. ASIO 腾空出世 那些年的网络库