第一件秘密武器:epoll/IOCP/kqueue新模型
传统的web服务器采用同步socket处理,即每一线程服务于一个客户(apache就是这样),或者是使用传统的select/poll模型。在连接数小的情况,性能也不会很差,但随着连接数的上升,性能会直线下降,超过一定数量时,会导致服务器无法提供服务。这就是著名的C10K问题。现代化的web服务器都采用效率更高的模型,linux下面是epoll,windows下面是IOCP,bsd系统的是kqueue. kangle新版也采用这种模型,连接数上升,性能只是会下降一点,基本上很稳定的提供服务。
第二件秘密武器: 非阻塞socket
即使采用了新模型也能更好的处理服务了,但为什么还要非阻塞socket呢?我们知道,对于发送数据(调用send),大多数的情况下是不会阻塞的,因为数据是直接放到socket的缓冲里面,只有缓冲满了的情况下会阻塞。问题就是我们已经使用了新模型可以检测到该socket能发的时候才发啊?和阻塞有什么关系呢?这就是关键了,因为新模型只能保证发送一次数据不会阻塞,你无法检测一次发送多条数据,这个很关键,因为你的数据不可能完整的,如http头和http body可能要分多次包发送。如果一检测只能发一次,效率会受些影响,如果我们用非阻塞socket,一次检测我们可以一直发送数据,多次调用send, 直到发完,或者检测到EAGAIN错误。这样节省epoll/kqueue系统调用次数,效率大大提升。
第三件秘密武器: writev代替write/send
上次我们已经优化了一次检测可以多次发送数据,节省了epoll/kqueue调用了,但还不够,够贪的吧,能不能做到一次检测,一次发送多条数据,这就是用writev,而不用传统的write/send调用了。大家可以man writev可以看到这个函数的帮助。我们可以在调用前构建多条数据数组,又节省几次write/send调用。IOCP的WSASend函数也是可以实现相同的功能。
第四件秘密武器: TCP_CORK/TCP_NOPUSH
下面我们从socket身上再下功夫,我们可以利用linux特有的TCP_CORK和bsd下面的TCP_NOPUSH功能。http协议层我们可以知道一次请求会发送多少数据给用户,但下面的tcp/ip层无法理解http协议,tcp/ip会按照它的算法,决定是否会合并一些小包发送。能否让 tcp/ip也能"理解"http协议?嗯,这就是第三件秘密武器了,TCP_CORK/TCP_NOPUSH,我们可以在开始发送http头前设置一下,设置一下socket的TCP_CORK/TCP_NOPUSH,相当于在数据流上加一个塞子,这样数据是不会发送到对方,我们再发送http其它数据,等这些数据全部发送完了,我们再把塞子拔掉,取消TCP_CORK/TCP_NOPUSH。节省带宽。至于TCP_CORK和TCP_NOPUSH的详细用法,大家可以搜索一下其它文档。
以上内容来源:http://bbs.tianya.cn/m/post-it-614856-1.shtml
个人在网络编程中总结:
1.线程个数要适中,最好采用线程池方式。
2.内存池自己管理
3.互斥锁越少越好, 即时要用,要采用读写分开的方式(双链表或双队列,一个用于读,一个用于写,读队列无数据再抢占写入队列,写队列可以积累数个包再一次性压入写队列),尽量减少lock的次数。