4 服务器优化
4.1 服务器整体性能考虑
对于价值昂贵的服务器来说,怎样配置才能发挥它的最大功效,又不至于影响正常的服务,这是在设计网站架构的时候必须要考虑的。常见的影响服务器的处理速度的因素有:网络连接,硬盘读写,内存空间,CPU速度。如果服务器的某一个部件满负荷运转仍然低于需要,而其他部件仍有能力剩余,我们将之称为性能瓶颈。服务器想要发挥最大的功效,关键的是消除瓶颈,让所有的部件都被充分的利用起来。
4.2 Socket优化
以标准的 GNU/Linux 为例。GNU/Linux 发行版试图对各种部署情况都进行优化,这意味着对具体服务器的执行环境来说,标准的发行版可能并不是最优化的[23]。GNU/Linux 提供了很多可调节的内核参数,可以使用这些参数为服务器进行动态配置,包括影响 Socket 性能的一些重要的选项。这些选项包含在 /proc 虚拟文件系统中。这个文件系统中的每个文件都表示一个或多个参数,它们可以通过 cat 工具进行读取,或使用 echo 命令进行修改。这里仅列出一些影响TCP/IP 栈性能的可调节内核参数[24]:
/proc/sys/net/ipv4/tcp_window_scaling “1”(1表示启用该选项,0表示关闭,下同) 启用 RFC[25] 1323[26] 定义的 window scaling;要支持超过 64KB 的窗口,必须启用该值。
/proc/sys/net/ipv4/tcp_sack “1”启用有选择的应答(Selective Acknowledgment),通过有选择地应答乱序接收到的报文来提高性能(这样可以让发送者只发送丢失的报文段);对于广域网通信来说,这个选项应该启用,但是这也会增加对 CPU 的占用。
/proc/sys/net/ipv4/tcp_timestamps “1” 以一种比重发超时更精确的方法(参阅 RFC 1323)来启用对 RTT 的计算;为了实现更好的性能应该启用这个选项。
/proc/sys/net/ipv4/tcp_mem “24576 32768 49152” 确定 TCP 栈应该如何反映内存使用;每个值的单位都是内存页(通常是 4KB)。第一个值是内存使用的下限。第二个值是内存压力模式开始对缓冲区使用应用压力的上限。第三个值是内存上限。超过这个上限时可以将报文丢弃,从而减少对内存的使用。
/proc/sys/net/ipv4/tcp_wmem “4096 16384 131072” 为自动调优定义每个 socket 使用的内存。第一个值是为 socket 的发送缓冲区分配的最少字节数。第二个值是默认值(该值会被 wmem_default 覆盖),缓冲区在系统负载不重的情况下可以增长到这个值。第三个值是发送缓冲区空间的最大字节数(该值会被 wmem_max 覆盖)。
/proc/sys/net/ipv4/tcp_westwood “1” 启用发送者端的拥塞控制算法,它可以维护对吞吐量的评估,并试图对带宽的整体利用情况进行优化;对于 WAN 通信来说应该启用这个选项。
与其他调优努力一样,最好的方法实际上就是不断进行实验。具体应用程序的行为、处理器的速度以及可用内存的多少都会影响到这些参数对性能作用的效果。在某些情况中,一些认为有益的操作可能恰恰是有害的(反之亦然)。因此,需要逐一试验各个选项,然后检查每个选项的结果,最后得出最适合具体机器的一套参数。
如果重启了 GNU/Linux 系统,设置的内核参数都会恢复成默认值。为了将所设置的值作为这些参数的默认值,可以使用 /etc/rc.local 文件,在系统每次启动时自动将这些参数配置成所需要的值。
在检测每个选项的更改带来的效果的时候,GNU/Linux上有一些非常强大的工具可以使用:
ping 这是用于检查主机的可用性的最常用的工具,也可以用于计算网络带宽延时。
traceroute 打印连接到特定网络主机所经过的一系列路由器和网关的路径(路由),从而确定每个 hop 之间的延时。
netstat 确定有关网络子系统、协议和连接的各种统计信息。
tcpdump 显示一个或多个连接的协议级的报文跟踪信息,其中包括时间信息,可以使用这些信息来研究不同协议的报文时间。
Ethereal 以一个易于使用的图形化界面提供 tcpump (报文跟踪)的信息,支持报文过滤功能。
iperf 测量 TCP 和 UDP 的网络性能;测量最大带宽,并汇报延时和数据报的丢失情况。
4.3 硬盘级缓存
硬盘级别的缓存是指将需要动态生成的内容暂时缓存在硬盘上,在一个可接受的延迟时间范围内,同样的请求不再动态生成,以达到节约系统资源,提高网站承受能力的目的。Linux环境下硬盘级缓存一般使用Squid[27]。
Squid是一个高性能的代理缓存服务器。和一般的代理缓存软件不同,Squid用一个单独的、非模块化的、I/O驱动的进程来处理所有的客户端请求。它接受来自客户端对目标对象的请求并适当地处理这些请求。比如说,用户通过浏览器想下载(即浏览)一个web页面,浏览器请求Squid为它取得这个页面。Squid随之连接到页面所在的原始服务器并向服务器发出取得该页面的请求。取得页面后,Squid再将页面返回给用户端浏览器,并且同时在Squid本地缓存目录里保存一份副本。当下一次有用户需要同一页面时,Squid可以简单地从缓存中读取它的副本,直接返回给用户,而不用再次请求原始服务器。当前的Squid可以处理HTTP, FTP, GOPHER, SSL和WAIS等协议。
Squid默认通过检测HTTP协议头的Expires和 Cache-Control字段来决定缓存的时间。在实际应用中,可以显式的在服务器端脚本中输出HTTP头,也可以通过配置apache的mod_expires模块,让apache自动的给每一个网页加上过期时间。对于静态内容,如图片,视频文件,供下载的软件等,还可以针对文件类型(扩展名),用 Squid 的 refresh_pattern 来指定缓存时间。
Squid 运行的时候,默认会在硬盘上建两层hash目录,用来存储缓存的Object。它还会在内存中建立一个Hash Table,用来记录硬盘中Object分布的情况。如果Squid配置成为一个Squid集群中的一个的话,它还会建立一个 Digest Table(摘要表),用来存储其它 Squid 上的Object摘要。当用户端想要的资料本地硬盘上没有时,可以很快的知道应该去集群中的哪一台机器获得。在硬盘空间快要达到配置限额的时候,可以配置使用某种策略(默认使用LRU:Least Recently Used-最近最少用)删除一些Object,从而腾出空间[28][29]。
集群中的Squid Server 之间可以有两种关系:第一种关系是:Child 和 Parent。当 Child Squid Server 没有资料时,会直接向 Parent Squid Server 要资料,然后一直等,直到 Parent 给它资料为止。 第二种关系是:Sibling 和 Sibling。当 Squid Server 没有资料时,会先向 Sibling 的 Squid Server 要资料,如果 Sibling 没资料,就跳过它向 Parent 要或直接上原始网站去拿。
默认配置的Squid,没有经过任何优化的时候,一般可以达到 50% 的命中率[30](图4)。如果需要,还可以通过参数优化,拆分业务,优化文件系统等办法,使得Squid达到 90% 以上的缓存命中率。 Squid处理TCP连接消耗的服务器资源比真正的HTTP服务器要小的多,当Squid分担了大部分连接,网站的承压能力就大大增强了。
4 某网站使用MRTG工具检测到的Squid命中率
蓝线表示Squid的流量,绿色部分表示Apache流量