高性能服务器的设计原则

在很多编程论坛里经常会看到有人讨论如何开发高性能服务器的问题,但是初学者往往会把精力纠结到API的使用上,错误的认为使用了一些高级的API就意味着高性能,属于只见树木不见森林。以下是我认为高性能服务器设计应该遵循的一些基本原则:

1. 有明确的服务器性能设计目标

在 不同应用场合中的服务器对性能的需求是不一样的,有些需要处理大量的并发连接,有些追求高实时性(低延迟),有些则追求高吞吐量,有些要求大量的IO操作 而有些则需要大量的CPU计算。所谓的高性能服务器设计就在于针对具体的性能要求给出专门的设计方案,而通用的适用于普遍场合的服务器设计那就不叫高性能 了,因此在设计你的服务器之前搞清楚你的性能设计目标是非常重要的,这将指导你做出正确的选择。

 

2. 合理的估算和分配服务器资源

服务器的资源包括:网络带宽、包吞吐量、CPU资源、内存资源等等。在任何时候服务器的资源都是有限的,制约性能的唯一因素就在于资源的瓶颈,而 要把性能最大的发挥出来就需要找出资源的瓶颈,并进行合理的分配和优化。这里举一个简单的例子:对于TCP连接来说,虽然它是抽象为数据流协议的,但是在 底层实现上是依赖于IP数据报协议,因此在估算服务器能处理的最大字节吞吐量的时候就不能简单的以网络带宽数据来估算,而是要根据IP包吞吐量 * 每TCP包大小来进行估算,在实际中还涉及到RTT(平均延迟时间)及TCP滑动窗口大小,nagle算法的采用等等因素,如果你每次TCP包的发送大小 只有几十字节的话,那么是远远达不到实际的理论带宽的,如果你的服务器是以字节吞吐量为设计目标的话,那么就需要想办法增加每个TCP包的发送大小。

 

3. 避免不必要的浪费

所 谓高性能是节省出来的,这是一句真理。几乎所有的程序员都是理性的,没有人会去刻意或者毫无道理的浪费系统资源。但往往我们会在不知不觉中浪费系统资源, 这主要源于我们的无知。由于编程语言、接口、库及框架将底层的细节抽象了,所以当我们只停留在这些抽象层次上,就很难认识到抽象背后隐藏的东西,在不知不 觉中浪费了系统资源。具体来说,每一个系统API的调用在程序上看只是一句函数调用而已,但是每个API背后的开销则是大不相同的,先来看一个简单的例 子:

在一个TCP数据包的构造中通常我们需要先发送一个头(里面可能只是简单的标识一下这个包的长度),然后再发送实际的内容,见代码:

send(socket, &packet_size, 4);
send(socket, packet, packet_size);

 

这 样看上去虽然只是2个简单的不起眼的API调用,但实际上却会造成很大的开销,send本身是一个昂贵的系统级调用,需要占用大量的CPU时间(send 的调用需要几到几十个us),同时第一send可能会导致底层构造并发送一个只带有4字节内容的TCP包,而一个TCP头就需要40字节,这严重的降低了 网络利用率。所以,如果我们把这两段数据拷贝到一个数据缓冲区并调用一次send发送的话,性能就会大大提升,这个例子同时暗示我们:如果有机会可以合并 更多小数据包并一次性调用send操作的话,那么性能将会有很大的提升。 

其他方面的例子也很多,例如线程切换的开销,cache missing的开销,cache一致性的开销,锁的开销等等。避免浪费听上去是一句简单的废话,但实际上告诉我们的是需要深入的了解抽象背后的细节。

4. 在延迟和吞吐量上做权衡

通 讯的延迟和吞吐量往往是矛盾的,我们可以通过一个简单的类比来解释这个道理:考虑一个邮差从A点送信到B点,假设用户每隔2分钟向A点的邮箱中投递一封邮 件,邮差从A点的邮箱中取出信件后赶往B地,路上需要10分钟时间,然后将信件放到B的分发点后返回A点,忽略邮差取信和发信所消耗的时间,如此循环往 复。 在这个例子里,用户的邮件送达B点的延迟最坏需要20分钟最好则需要10分钟,邮差一个来回需要20分钟平均可以送10封信,因此邮差一个来回的开销可以 达到的吞吐量为10。接着,我们改变一下条件:让邮差在返回A点后等待10分钟后再向B出发,于是邮件送达B点的延迟变为最坏30分钟最好10分钟,现在 邮差一个来回可以送15封信,吞吐量变大了。在网络通讯中,数据包的构造、传送和接收是一个有很大开销的操作,只有尽可能多的在一次传输中传送更多的数据 才能提高吞吐量,在实际的测试中一次TCP发送的数据量至少需要超过1KB,才能接近理论数据吞吐量。但在实际中,一次用户数据的发送量往往很小(这取决于应用的类型),如果人为的加上一定等待和缓冲,就可以达到以时间换空间的效果。

 

5. 要为最坏和满负载情况做设计 

“稳 定压倒一切”,对于服务器来说是一句至理名言。服务器的资源是有限的,所能承载的最大负载必然是有限的。正如前段时间杯具的12306铁路网络售票系统, 想必很多人都深有体会(可惜我从来没有体会过春运)。在服务器超负荷运行中最杯具的就是称之为"雪崩效应"的一类问题,当负载达到一个临界点后服务器性能 急转直下,使得正常的服务也无法进行甚至直接宕机。因此,作为一个有职业素养的服务器端程序员(非临时工和无证程序员),在设计中必须对各种最坏情况要有 预计,并通过前期设计及后期的压力测试来确定服务器所能达到的满负载指标,对超负载情况要有保护措施(拒绝服务新的连接以保证服务器的安全),当然在实际 的运营中还需要为服务器保留一定的安全边界以防止各种颠簸酿成杯具。
posted on 2012-02-03 10:57  啊T  阅读(1241)  评论(1编辑  收藏  举报