今天拜读了杨建的博客, 杨建开发的程序以高请求,高带宽为主,比如:

开发的一系列系统中的两个 并发达到54.15w req/s , connections 340.25w  高峰一小时近20亿实际http请求处理量。

所以整理了一些观点如下,喜欢吃快餐的请进。由于整理下面内容是跨十几篇文章的,就不一一给出链接了,需要看原文的简单Google一下即可找到。

一 、如何衡量Web Server的性能指标

总体来说同时在线connections和当时的每秒请求处理量是两个最重要的指标。

实验环境数据: 杨建曾写了个HTTP服务框架,不使用磁盘I/O,简化了逻辑处理部分,只会输出 “hello world!”  程序部署在192.168.0.1上(2cup*4Core,硬件和系统都做过优化),在另外8台同等配置服务器上同时执行命令  ./apache/bin/ab -c 1000  -n 3000000   -k  “http://192.168.0.1/index.html” 几乎同时处理完毕,总合相加 40w req/s,他相信这是目前硬件水平上的极限值 。

二、部署经验

I. 对于cache处理的创新

通过在url后面增加?maxage=xxx的做法,服务器通过这个参数来返回cache失效的maxage,比较灵活,优点:

  1. 可以控制HTTP header的的 max-age 值。
  2. 让用户为每个资源灵活定制精确的cache时间长度。
  3. 可以代表资源版本号。

好奇,试验了一下杨建的系统
$ curl -i http://sports.sinajs.cn/today.js?maxage=22

HTTP/1.1 200 OK
Server: Cloudia
Last-Modified: Tue, 28 Apr 2009 14:10:02 GMT
Cache-Control: max-age=22
Content-Encoding: deflate
Content-Length: 257
Connection: Keep-Alive
Content-Type: application/x-javascript

大家可以看看返回的字段,其中有一点很有趣,返回直接不理http请求强制返回deflate(zip)格式, 比较霸道。

II. IDC分布

关于网络环境与IDC分布,正在迅速成长的公司可以了解下原文杨建介绍的经验,非常详尽。为什么说迅速成长的公司需要看,因为大的网络公司已经很清楚了,小的公司估计也用不上 :)

III. DNS解析经验

DNS解析有有个缺陷,每个单独域名里写在最前面的那个ip,它被轮询到的概率要比同组的服务器高10%,而且随着同组服务器的增多,这个差距会变大。所以最解析时候,每个IDC最好把硬件性能最好的服务器ip放在最前面。

IV. 优化网卡, 调整内核参数

这两段介绍也很有价值,主要是一些参数调优,做大型系统的推荐去看一下原文

三、开发经验

I. 关于epoll

epoll最擅长的事情是监视大量闲散连接,批量返回可用描述符,这让单机支撑百万connections成为可能。
边缘触发ET 和 水平触发LT 的选择:
早期的文档说ET很高效,但是有些冒进。但事实上LT使用过程中,苦恼了将近一个月有余,一不留神CPU 利用率99%了,可能是我没处理好。后来zhongying同学帮忙把驱动模式改成了ET模式,ET既高效又稳定。
简单地说,如果你有数据过来了,不去取LT会一直骚扰你,提醒你去取,而ET就告诉你一次,爱取不取,除非有新数据到来,否则不再提醒。

自己用epoll写C的可以去深入了解下。

II. 写数据

顺便再说下写数据,一般一次可以write十几K数据到内核缓冲区。
所以对于很多小的数据文件服务来说,是没有必要另外为每个connections分配发送缓冲区。
只有当一次发送不完时候才分配一块内存,将数据暂存,待下次返回可写时发送。
这样避免了一次内存copy,而且节约了内存。

III. 如何节约CPU

HTTP请求预处理 (预压缩,预取lastmodify,mimetype)
预处理,原则就是,能预先知道的结果,我们绝不计算第二次。

预压缩:我们在两三年前就开始使用预压缩技术,以节约CPU,伟大的微软公司在现在的IIS 7中也开始使用了。所谓的预压缩就是,从数据源头提供的就是预先压缩好的数据,IDC同步传输中是压缩状态,直到最后web server输出都是压缩状态,最终被用户浏览器端自动解压。

IV. 怎样使用内存

1. 避免内核空间和用户进程空间内存copy (sendfile, splice and tee)
sendfile: 它的威力在于,它为大家提供了一种访问当前不断膨胀的Linux网络堆栈的机制。这种机制叫做“零拷贝(zero-copy)”,这种机制可以把“传输控制协议(TCP)”框架直接的从主机存储器中传送到网卡的缓存块(network card buffers)中去,避免了两次上下文切换。据同事测试说固态硬盘SSD对于小文件的随机读效率很高,对于更新不是很频繁的图片服务,读却很多,每个文件都不是很大的话,sendfile+SSD应该是绝配。

2. 内存复用  (有必要为每个响应分配内存 ?)
对于NBA JS服务来说,我们返回的都是压缩数据,99%都不超过15k,基本一次write就全部出去了,是没有必要为每个响应分配内存的,公用一个buffer就够了。如果真的遇到大数据,我先write一次,剩下的再暂存在内存里,等待下次发送。

V. 减少磁盘I/O

共享内存可以考虑用BDB实现,它取数据的速度每秒大概是100w条(2CPU*2Core Xeon(R) E5410 @ 2.33GHz环境测试,单条数据几十字节),如果你想取得更高的性能建议自己写。