大规模网站基础设施和 Drupal

作者:Jerad Bitner

当 Twitter 宕机时,用户会看到臭名昭著的“失败鲸鱼”错误信息,这是一个小鸟们努力将一只卡通睡鲸吊到空中的插图,并配有文字“推文太多了!请稍等片刻再试一次。” 这种情况经常发生,Twitter 为此有一个广为人知的插图。不久前,许多读者可能还记得 Facebook 宕机了好几天。诚然,这些网站正在处理非常大的流量,但较小的网站也经常面临同样的问题。这是为什么呢?首先,网站不再是静态页面的集合。如今,网站将社交网络功能与为 individual 用户高度定制的内容相结合,这意味着大多数页面都必须即时组装。其次,内容正在变化——富媒体、在线广告、视频、电话。通过管道传输的内容不仅仅是文本,网络流量只会持续增长。应对复杂性和负载的这种组合是许多不断增长的社交媒体网站存在的祸根。接下来是一些巧妙的方法来解决这个巨大的问题。

令人惊讶的是,无论网站构建的技术如何,大多数扩展问题的解决方案通常是相同的。Lullabot(本文作者的母公司)是一家 Drupal 开发公司,这意味着我们的大部分经验都集中在典型的 LAMP 堆栈(Linux、Apache、MySQL 和 PHP)上,尽管大多数技术是通用的,并且一些最先进的性能软件是平台中立的。

服务器基础设施

扩展网站的主要因素之一当然是硬件(图 1)。系统管理员总是可以投入更多硬件来解决问题,至少可以暂时解决问题,如果他们有资源这样做的话。在需要这样做之前,可以部署相当多的服务,开发人员可以通过减少或优化查询来有选择地优化应用程序。然而,当涉及到短时间内大量的用户和带宽时,几乎总是需要将硬件纳入其中。这就是为什么以一种可以快速向上扩展以应对流量高峰,并在流量消退时缩减的方式规划您的硬件基础设施非常重要的原因。

Large-Scale Web Site Infrastructure and Drupal

图 1. 硬件堆栈

典型的设置,无论是虚拟的还是专用的,通常包括多个 Web 服务器、多个数据库服务器,有时甚至包括单独的缓存服务器,所有这些服务器都位于负载均衡器之后,负载均衡器在机器之间分配流量。根据其处理器速度和可用内存量,Web 服务器或数据库通常可以兼作缓存服务器,因为缓存服务通常比 Apache 或 MySQL 需要更少的资源。

虽然跨多个 Web 服务器或 Web 前端分配流量可能是一个快速的胜利,但它可能会引入文件上传管理的问题。如果请求由负载均衡器以轮询方式分配,则用户可能在一个服务器上上传文件,然后在上传后切换到另一个没有新上传文件的 Web 服务器。为了解决这个问题,还会在组合中添加文件服务器。文件服务器通常是某种形式的 NAS(网络附加存储)或 NFS(网络文件系统)挂载,允许应用程序在机器之间共享文件。每个 Web 前端都将在 Web 根目录中存储应用程序的副本,但是当涉及到用户上传或经常更改的应用程序文件时,NFS 挂载会将所有服务器连接到共享文件位置。

缓存技术

扩展网站的另一个主要因素当然是软件(图 2)。为了有效地扩展,高流量网站需要某种或多种缓存。缓存机制不是相互排斥的,大多数知名网站都结合使用了多种缓存。大多数类型的缓存都试图减少呈现页面或将高级语言编译成字节码所需的磁盘访问量,以便它们运行得更快——越接近机器语言越好。

Large-Scale Web Site Infrastructure and Drupal

图 2. 软件堆栈

APC(Alternative PHP Cache)和其他 opcode 缓存使 Web 服务器不必在每次请求时都读取、解析和编译 PHP 文件。APC 是一个免费的开源 opcode 缓存,并且几乎是标准。它将内置于 PHP 6 中,但有许多不同的缓存,它们的性能有所不同。

现代内容管理系统(如 Drupal)可能会在每个页面请求上进行大量数据库调用。由于对数据库的调用会访问磁盘,因此通常会成为瓶颈。Memcached 是一项服务,允许将整个数据库表存储在内存中,从而显着加快对这些表的查询速度并减轻数据库的压力。它的行为就像一个巨大的哈希表,并从内存中提供这些数据。Memcached 是免费的、开源的,并且被大量高流量网站使用。在大多数典型设置中,Memcached 与 MySQL 一起安装在数据库服务器上。但是,如果 Memcached 和 MySQL 共享此关键资源,则数据库服务器需要有大量可用的 RAM。在某些情况下,Memcached 实际上被放置在自己的服务器上,与数据库服务器完全分离,这防止了 Memcached 使用数据库服务器的过多内存。

Varnish 是一款出色的高性能 HTTP 加速器。Varnish 的技术术语是“反向代理缓存”,这意味着当您访问网站时,它会处理请求。如果 Apache 是医生,Varnish 就是分诊护士。在每个匿名页面请求发出后,Varnish 会在超快速存储中创建页面副本,以便下次请求该页面时,它会立即返回该页面,从而绕过 Apache、PHP、MySQL、Memcached 或您的网站可能需要的任何其他技术来提供页面。如果 Varnish 没有请求的文件或页面的副本,它会将请求发送到 Apache。如果您要提供静态内容,那真是一个巨大的胜利。

外包搜索

搜索是资源密集型的。优化搜索将有助于提高网站的整体性能,并且是一个很棒的外包到另一个盒子的过程。Solr 可以帮助缓解 Web 服务器的负担。Solr 是 Apache 基金会的一个项目,它利用了 Lucene 的强大功能(一个出色的索引器和搜索器),并将其作为 Web 服务公开。使用 HTTP POST 和 GET 请求,您可以将文档馈送到 Solr 进行索引,并发出查询进行搜索。在 Drupal 中,Views 模块充当可视化查询构建器并处理搜索。借助 Drupal 中的 Views 3,您可以插入 Solr 来处理搜索的繁重工作,而不是让 Drupal 为此访问 MySQL,从而减轻数据库服务器的负载,最好将其留给像 Lucene 这样的文档索引器。

调整 Apache

Apache 的 MaxClients 设置限制了可以同时处理的请求数量。如果达到此限制,用户必须等到子进程被释放后才能连接。但是,如果此数字增加得太多,则存在 Web 前端内存耗尽的风险。有一个标准公式可以根据机器可用的 RAM 计算出此设置应该是什么

  • 公式:RAM/平均 Apache 使用内存大小 = # 最大客户端数

  • 示例:2GB/20MB = 100 MaxClients

Apache 的 mod_expires 设置控制通过 Apache 提供给您的机器的任何内容的 HTTP 标头信息。如果资源已缓存在用户的计算机上,则此设置可以告诉对该资源的任何后续请求,该资源是否已过期并且需要再次下载。对于 text/HTML 标头类型,最好启用此设置

<IfModule mod_expires.c>
  ExpiresActive On
  ExpiresDefault A1209600
  ExpiresByType text/html A1
</IfModule>

KeepAlive 设置是一种告诉 Apache 将 HTTP 连接保持活动一段时间以便可以重用的方法。这已被证明可以使具有许多图像的 HTML 文档的延迟时间缩短近 50%。启用此功能并将 KeepAliveTimeout 设置为 2 秒

KeepAlive On
KeepAliveTimeout 2
优化 MySQL

MySQL 是 Drupal 最广泛使用的数据库,尽管 Drupal 6 也支持 Postgres。Drupal 7 具有面向对象的数据库抽象层,允许为许多其他数据库系统编写驱动程序。在 MySQL 的配置中,有一些关键事项需要牢记,这些事项可以帮助优化应用程序的性能。

MySQL 具有默认启用的内置查询缓存。确保为此缓存提供充足的内存

[mysqld] query_cache_size=32M

构建应用程序后,最好在短时间内记录慢查询,以获取耗时较长的查询列表,并可以使用 EXPLAIN 进行检查,然后进行优化

log-slow-queries = /var/log/slow_query.log
long_query_time = 5
#log-queries-not-using-indexes

MySQL 的 EXPLAIN 命令是一种很好的方法,可以准确了解特定查询正在做什么,以便了解为什么评估和返回结果可能需要很长时间的一些线索。要查看的关键事项之一是 EXPLAIN 告诉您它必须搜索的行数。这可能表明您的某个表已满,是新索引的良好候选表。

查看以下查询,我们看到有三个字段可以放置索引,以减少查询必须搜索的行数才能找到所需结果

...
FROM node node
WHERE node.status = 1
AND node.type IN ('story')
ORDER BY node.created DESC

状态、类型和创建字段是此查询结果的关键,可以对其进行索引,以便将它们视为一组

mysql> ALTER TABLE node ADD INDEX (status, type, created);

表锁定可能是一个性能难题。默认情况下,Drupal 的 MySQL 数据库表都设置为 MyISAM。由于 MyISAM 在查询期间锁定整个表,因此当某个表不可用或被锁定时,高流量可能会导致 MySQL 错误。如果您开始看到这些错误,请查看哪些表正在给出错误,并评估是否应将它们设置为 InnoDB。InnoDB 执行行锁定而不是表锁定。在评估时,请查看表是否具有任何 auto_increment 字段,并记住转换此表可能会导致 INSERT 变慢,因为 InnoDB 在 INSERT 上执行完全表锁定以避免键重复。

PHP

静态变量缓存是 PHP 中的快速简便的胜利。这是一个具有简单数据库查询的简单函数的示例

function taxonomy_get_term($tid) {
  return db_fetch_object(
    db_query('SELECT * FROM {term_data} WHERE tid = %d', $tid)
  );
}

可以为这个函数提供一个简单的静态变量,这样如果这个函数在一个页面加载中被多次调用,它可以跳过对数据库的调用,并从这个静态缓存中提供结果

function taxonomy_get_term($tid) {
  static $terms = array();
  if (!isset($terms[$tid])) {
    $terms[$tid] = db_fetch_object(
      db_query('SELECT * from {term_data} WHERE tid = %d', $tid)
    );
  }
  return $terms[$tid];
}
应用程序 (Drupal)

Drupal 是 Lullabot 用来在其基础设施之上构建高性能网站的内容管理框架。Drupal 以 PHP 作为其主要编程语言构建,并有大量用户贡献的模块可免费使用以扩展其功能。因此,它被比作乐高积木,并且由于模块的质量参差不齐,因此最好对选择包含到任何平台构建中的任何模块进行完整的代码审查。如果现有模块已经完成了大部分所需的功能,则应对其进行审查,以确保利用了静态变量缓存、优化了查询并使用了通用编码标准。

当发现模块在这些方面中的任何一个方面都缺乏时,或者通过模块的问题队列(可以在您下载模块的同一页面上找到)发现任何一般错误时,定期将补丁贡献回模块。一旦站点构建完成,性能审查也是一个好主意,以确保查询得到优化并且每个页面加载运行不超过一次。Devel 模块是这方面的一个很好的资源,因为它会为您提供页面加载时间、内存使用情况的统计信息,并且可以显示在任何给定页面加载上执行的每个查询。

除了常规的 LAMP 配置优化、缓存技术和硬件基础设施之外,Drupal 中还提供一些通用的 Web 开发最佳实践,这些实践不仅可以减少各种服务器上的负载,还可以轻松地将您的某些数据结构放在代码中,这些代码可以进行版本控制,以跟踪更改并帮助部署这些更改的过程。第一个也是相对较新的“可导出项”范例是双重的,因为它为您提供了一种从代码而不是数据库读取数据结构的方法,并且它也可以部署到不同的环境并重用。

可导出项始于 Earl (merlinofchaos) Miles 的 Views 模块,他希望找到一种方法来帮助调试他的模块用户可能遇到的问题。因此,他创建了一种让用户将他们创建的视图导出到可读数据结构的方法,然后他可以将其放在自己的机器上以帮助他调试。这不仅具有与其他用户共享这些“视图配方”的绝妙副作用,而且还演变成一种方法,其中结构可以替换从数据库读取的内容并帮助提高性能。然后将可导出项外推到一个名为 Ctools(Chaos Tools 的缩写)的库中,并用于 Panels 模块。其他人开始注意到并为他们的模块实施可导出项,现在有大量模块为此目的使用了 Ctools Exportables。

这最终导致了一个名为 Features 的模块,该模块提供了一个 UI 来选择 Drupal 安装中的各种可导出数据结构,并将它们包装成一个自定义的“功能”模块,然后可以共享该模块。这些功能可以是简单的配置选项,也可以是复杂的功能,需要许多其他贡献的模块才能为任何 Drupal 网站提供功能丰富的增强功能。它不仅可以用于共享此类功能,而且还已成为在创建现代 Drupal 网站的部署过程中不可或缺的一部分。

最近成熟并成为任何专业 Drupal 开发人员的必需品的另一个工具是 Drush。Drush 代表 Drupal Shell,是一种通过命令行控制 Drupal 网站的方法。它不仅提供了强大的命令来快速操作您的网站,而且其他模块也可以提供与 Drush 的集成,从而创建与使用其特定模块相关的自己的命令。例如:Features 模块为 Drush 提供了命令,使您可以快速列出、更新和恢复作为 Drupal 安装代码库一部分的任何功能模块。Backup and Migrate 模块提供了集成,使您可以使用简单的命令快速创建网站的 SQL 备份。一些模块甚至提供了与 Drupal 和 Git 一起使用的命令!因此,Drush 不仅允许您快速处理 Drupal 站点,而且您也不必通过 Apache 加载巨大的页面来执行此操作。

当然,任何专业的网站都离不开版本控制。Lullabot 已经使用了 CVS(Concurrent Versions System)、SVN(Subversion),最近又转向了 Git。但是无论您使用什么,备份您的工作和为在同一项目上工作的团队进行版本控制都很重要。代码版本控制的好处很多。构建高性能网站通常需要很多人,因此版本控制成为必需品。

Jerad Bitner 自从从 4.6 升级到 4.7 的噩梦般升级(如果您问的话,那是 2005 年初)以来,一直使用 Drupal。他最初是 C/S Group 的一名技术插画家,并在 Photoshop、Illustrator、AutoCad 和 Macromedia 产品以及 PHP 方面工作了三年。当需要跨公司不同地点复制平台时,Jerad 找到了 Drupal,并且从此一发不可收拾。

Nate Haug 为 Lullabot 增添了一丝设计感。他获得了杜鲁门州立大学的美术和计算机科学学位,在技术和美学之间架起了完美的桥梁。细节是他的痴迷,所以如果您知道自己想要什么,Nate 将满足您的愿望。

加载 Disqus 评论