最近kangle web服务器已经发布了新版2.3.1,其性能比老版本提升8倍之多,静态文件处理能力达apache的8-10倍。如此高的性能怎么来的 呢?kangle有哪些秘密武器呢?其实作为现代化的其它web服务器如nginx,lighttpd,cherokee等其原理都差不多,可能实现细节 有些区别而已。
第一件秘密武器: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的 详细用法,大家可以搜索一下其它文档。
第五件秘密武器: 缓存(Cache)
缓存(Cache)可以大大提高我们的性能,http协议已经有缓存机制。但实现比较复杂,kangle新版采用了内存和磁盘两级缓存机制,磁盘缓 存主要用在反向代理等要远程连接的地方,对于本机的文件,主要使用内存缓存。缓存淘汰机制使用LRU算法。kangle内部维护两个链表,一个是map 表,一个是双向链表,map表查找时使用,一但某个网页再次请求(命中),就会插到双向链表的最前面。每次要删除缓存网页时从双向链表的后面开始,即那些 最近没有命中的网页。
第一件秘密武器: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的 详细用法,大家可以搜索一下其它文档。
第五件秘密武器: 缓存(Cache)
缓存(Cache)可以大大提高我们的性能,http协议已经有缓存机制。但实现比较复杂,kangle新版采用了内存和磁盘两级缓存机制,磁盘缓 存主要用在反向代理等要远程连接的地方,对于本机的文件,主要使用内存缓存。缓存淘汰机制使用LRU算法。kangle内部维护两个链表,一个是map 表,一个是双向链表,map表查找时使用,一但某个网页再次请求(命中),就会插到双向链表的最前面。每次要删除缓存网页时从双向链表的后面开始,即那些 最近没有命中的网页。
来源:oschina
链接:https://my.oschina.net/u/168948/blog/30139