返回 登录
4

服务器负载优化技巧

原文:Tips and Tricks for Reducing Server Load
作者:Thayne McCombs
翻译:Vincent

译者注:作者以字体服务为例,首先分析了为什么服务器的负载会那么高。找到原因以后,将服务器端的部分工作进行转移,从而降低了服务器端的负载。以下为译文。

我们有一个面向服务的架构。其中一个服务是字体服务,它负责根据family和Unicode提供字体数据,并检查用户上传的字体的权限。我们没有想到字体服务具有特别高的负载(负载是使用或等待CPU的平均线程数)。然而去年我们注意到字体服务的负载令人惊讶,特别是在访问量很少的夜晚。幸运的是我们能够找到根本原因,并大大增加我们的字体服务的性能和整个系统的稳定性。接下来介绍一下我们是如何做到的。




图1:更改前后的服务器负载。(Fix于12月21日发布)

使用火焰图s进行调试

我的一位同事在Netflix的Brendan Gregg发现部署了一个灵活的火焰图形工具。这个工具结合了很多分析工具的数据,从而把本地和JVM函数使用的资源展示在单个图形中。图中的每个矩形表示单个堆栈帧-矩形的宽度表示使用了多少资源(如CPU时间),y轴表示调用堆栈。要找到有问题的功能,你只需要找出宽矩形就可以了。该工具在调试字体服务时的性能表现是非常不错的。




图2:高负载时字体服务器的火焰图

在高负载状态下,我们从字体服务收集了几个火焰图。这里显示了其中的一个,还包括了火焰图JVM部分的特写。我们很快就发现这些火焰图中的一个图案。大部分时间都用于libz.so(用于GZIP压缩/解压缩),并且JVM中的大部分时间都消耗在了XML转义和UTF-8编码。




图3:火焰图中JVM特写部分

为什么如此之慢

首先,这里介绍了字体服务是如何工作的背景。我们将字体数据存储在Amazon S3存储区中,为每种字体的每个Unicode范围提供单独的对象。其他服务将请求字体数据的字体组、一组Unicode范围和一个用户。字体服务将为用户访问的每个字体下载指定范围的数据,然后返回包含所有数据的XML响应。

这个功能非常简单,并没有什么明显的密集的计算。然而,负载还是比较高的。通过火焰图,我们可以看出libz,XML转义和UTF8编码都占用了大量的CPU。但是为什么编码以及压缩会如此耗时呢?还记得我说过晚上的负荷是最高的吗?好吧,我们(美国山区时间)是夜晚时,亚洲可能是白天。虽然我们这边晚上的访问量较少,但是其他地方的用户在这段时间内正在使用中文,日文和韩文等亚洲语言的Unicode系列。结果显示,这些范围的字体数据的访问量要大得多。这些数据是使用gzip解压缩,用UTF-8解码,然后XML转义,UTF-8编码和GZIP压缩。对于类似于拉丁语这种小范围的,这个过程可能并没有什么影响。然而,单个中文、日文或韩文的范围比拉丁语范围大2个数量级(前者相当于1MB,后者是60KB)。对于这些范围较大的,所有这些转换都会影响CPU。Gzip压缩和解压缩代价相对比较高,XML转义并不是太高。

我们是如何使它变快的

来自字体服务的响应内容,从本质上看来,仅仅是S3的原始数据的集合。字体服务确实执行了一些重要的附加任务,如权限检查和从字体系列的名称查找字体键。然而,字体服务也不是说就必须得从S3代理字体数据。我们的解决方案很简单。我们只是简单地回应了包含字体数据的S3对象的直接链接列表,而不是在字体服务中下载,再重新编码字体数据。

这样更改以后,字体服务器上的负载减少到几乎什么都没有(参见图1),它对客户端服务的响应也没有明显的影响。尽管我们的第一次尝试是成功的,但我应该注意到,将它部署在一个特性标志上,它可以让我们在请求的百分比上进行测试,以测试它的工作是否成功,然后将其100%地转化为100%。

结论

通过对生产服务器进行分析,我们能够找出那些服务器端没有必要进行处理的工作,并且去掉这些冗余的工作。以下是我们总结的一些经验。

  1. 使用诸如火焰图形之类的分析工具找出那些占用CPU的功能。

  2. 压缩和其他编码的代价很高。

  3. 如果客户端能够直接访问数据,通过发送链接可以提高整体性能,而不是代理数据。(免责声明:这也不是像那些神奇的子弹一样任何情况下都能通用,在某些情况下,在某些情况下可能会对客户端的性能造成伤害,因为它必须要发出第二个请求。)

评论