Web 缓存,第 2 部分

作者:David Guerrero

上个月我们讨论了代理服务器和缓存的基本概念。现在,让我们看看如何在您的组织中实施这项技术。市面上有一些代理服务器程序,例如 MS-PROXY,又名 Catapult,仅适用于 Windows NT,以及 Netscape Proxy Server,适用于不同的 UNIX 平台和 Windows NT。两者都有两个主要缺点:它们是商业软件,并且不支持 ICP。优秀的 Apache Web 服务器自 1.2 版本以来就包含了一个代理缓存模块。这个模块是一个非常有趣的选择:它是免费的,并且可以与互联网上最流行的 Web 服务器一起工作。然而,它不使用 ICP,并且其稳健性无法与代理缓存服务器的最佳选择——Squid 相提并论。

Squid 是一个高性能代理缓存服务器,源自 Harvest Research Project 的缓存模块,由 Duane Wessels 维护。它支持 FTP、gopher、WAIS 和 HTTP 对象。它将热门对象存储在 RAM 中,并在磁盘目录中维护一个强大的对象数据库。Squid 还支持 SSL 协议,用于代理安全连接,并具有复杂的访问控制机制。Squid 的另一个有趣功能是负缓存,它会在短时间内(通常为五分钟)保存“连接被拒绝”和“404 未找到”回复。

Squid 由四个程序组成

  • squid:主代理服务器

  • dnsserver:一个 DNS 查找程序,执行单次、阻塞式 DNS 操作

  • unlinkd:一个在后台从缓存目录中删除文件的程序

它还提供了一个 CGI 程序,旨在通过 Web 界面运行,输出有关其配置和性能的统计信息,并允许一些管理功能。

Squid 安装

安装 Squid 很简单。只需从 http://squid.nlanr.net/ 下载源代码存档,并在临时目录中键入

gzip -dc squid-x.y.z-src.tar.gz | tar xvf -

接下来,通过键入以下命令编译和安装软件

cd squid-x.y.z
./configure
make all
make install

这些命令会将所有需要的程序和配置文件安装到 /usr/local/squid。二进制程序安装在 /bin 目录中,配置文件安装在 /conf 中。日志文件位于 /logs 目录中,对象数据库位于 cache 目录及其子目录中。一个名为 RunCache 的 shell 脚本位于 bin 目录中,用于运行 squid 二进制文件,并确保如果进程因任何原因死亡,它会自动重启。因此,将以下行放入您的 rc.local 文件中

/usr/local/squid/bin/RunCache &

如果 Squid 由于某些配置问题而无法启动,这将在 /usr/local/squid/squid.out 中生成一个错误日志。

当然,如果您使用 RedHat Linux 或其他支持 RPM 包的发行版,您可以选择安装 Squid 的 RPM 版本。

Squid 安装了一个名为 squid.conf 的示例配置文件,其中包含每个选项的许多注释。在这里,您可以更改 ICP 和 HTTP 端口(默认为 3128),并定义为缓存对象预留多少内存和磁盘空间,以及其他参数,例如刷新模式和访问控制限制。当然,只有当您的缓存将成为其他缓存的同级或父级时,才需要 ICP 端口。用于更改这些值的指令是 http_porticp_portcache_dircache_swap。此外,您可以设置要存储在数据库中的最大对象大小;默认值为 4MB。另外,您应该取消注释此文件中的以下行

cache_effective_user nobody
cache_effective_group nobody

这避免了以 root 身份运行 Squid,对于任何运行像 httpdgopherd 这样的服务器的人来说,这都是一个危险的习惯。如果您使用的是最新版本的 Squid(在撰写本文时,当前版本是 1.1.16),它将不会以 root 身份启动,而是会将错误消息写入 squid.out 文件。

为了让 Squid 使用您 100MB 的硬盘,cache_dir 指令应该像这样

cache_dir /usr/local/squid/cache 100 16 256

在首次启动 Squid 之前,创建缓存和日志目录。要构建缓存和哈希子目录,您应该执行以下命令

cd /usr/local/squid
mkdir cache
chown -R nobody cache
cd /usr/local/squid/bin
./squid -z

最后,创建并更改日志目录的所有者

cd /usr/local/squid
mkdir logs
chown nobody logs

现在可以首次安全地运行 Squid,使用上面的 RunCache 调用。它将生成多个 dnsserver 进程,并将其 PID 写入文件 logs/squid.pid。重要的警告或错误消息可以在 squid.out 和 logs/cache.log 文件中找到。请记住,如果您想关闭缓存,您必须首先杀死 RunCache 进程以避免立即重启,然后键入

/usr/local/squid/bin/squid -k shutdown

永远不要使用 kill -9 来关闭缓存,因为它不会以可以恢复的方式关闭对象数据库——您可能会丢失其中的一部分。

限制对您的缓存的访问

为了仅允许您组织中的用户访问您的缓存,您必须设置一些访问控制列表 (ACL)。在 Squid 中定义访问列表非常容易;所有访问列表都使用名称定义,并用于定义元素的子集。您可以创建 IP 地址、协议、目标 URL 甚至浏览器品牌的子集。定义 ACL 或子集的指令是

acl

您可以在示例 squid.conf 中了解有关 ACL 类型的更多信息。在限制仅对我们的用户访问的情况下,需要的类型是 src。例如,假设您要允许 172.16.236.0 C 类、下一个 C 类的前 32 个地址以及您的 PC 172.16.237.180 中的所有浏览器访问缓存。您可以像这样定义 ACL

acl my_users src 172.16.236.0/255.255.255.0
acl my_users src\
172.16.237.1-172.16.237.32/255.255.255.255
acl my_users src\
172.16.237.180/255.255.255.255

接下来,为其余地址定义 ACL。此行包含在 squid.conf 示例文件中

acl all src 0.0.0.0/0.0.0.0

使用 http_access 指令以有序方式应用这些 ACL。语法是

http_access

例如

http_access allow my_users
http_access deny all

多个 ACL 可以组合在同一个 http_access 指令中,并且可以以否定形式使用(即,以 ! 开头)。所示示例是 ACL 最简单的用法,但更复杂的形式将仅允许在指定的小时和日期进行连接,仅允许提取定义的 URL 或域,并限制某些协议(例如 FTP)。Squid 的这一强大功能可以帮助您执行和实施您的安全策略,无论您是在防火墙中使用 Squid,还是 Squid 机器是唯一允许穿过防火墙的机器。只需在 squid.conf 中查找示例即可。

还有一个 ACL 允许设置您允许用户使用的所需 Web 端口。这是 Safe_ports ACL。您应该取消注释此行并将 443 端口添加到此 ACL,以便允许通过您的 Squid 服务器使用安全 Web 服务器。

查看日志

Squid 可以生成关于您的代理缓存使用情况的大量日志。借助这些信息和一些脚本的帮助,我们可以生成完整的访问统计信息,就像从 Web 服务器生成的那样。Squid 维护三个主要的日志文件

  • cache_log 包括有关缓存状态和操作问题的警告和信息。

  • store_log 包括有关数据库操作的信息,例如新项目的插入和过期对象的释放。

  • access_log 包含从缓存中提取的每个对象的条目以及有关如何提供服务的信息。它还包括有关缓存从使用此服务器作为邻居的其他服务器收到的每个 ICP 查询的信息。

许多实用程序可用于从 access_log 文件生成统计信息(请参阅资源)。请记住,查看您的 access_log 以了解您的用户访问了哪些地方是不道德的。一些站点已选择不以任何形式发布处理后的统计信息,以保护其用户的隐私,这对于我们所有参与 Internet 社区的人来说都是一个重要的关注点。

日志增长非常快,几天之内就会耗尽您剩余的磁盘空间。为了安全地清理您的日志文件,您应该使用 SIGUSR1 信号轮换它们。可以在您的 crontab 中添加一行,以便每晚开始新的日志文件

/usr/local/squid/bin/squid -k rotate

此命令将创建文件 access_log.0、store_log.0 和 cache_log.0,并开始记录到新的空文件中。现在您可以安全地删除这些文件或处理它们以进行统计。下次您轮换日志时,files.0 将被移动到 files.1,依此类推。您可以使用 squid.conf 文件中的 logfile_rotate n 指令配置 Squid 将使用多少个扩展名进行这些轮换,以节省磁盘空间。

配置浏览器以使用缓存

要开始使用您的新代理缓存服务器,您必须首先指示用户的浏览器从您的服务器获取对象,而不是直接检索它们。在大多数现代 Web 浏览器中,配置选项之一是代理设置的规范。另一个选项是指定必须通过代理获取的域或 URL 模式列表。

在 Netscape Navigator 或 Communicator 中,您可以为要代理的每项服务包含代理服务器及其端口。使用 Squid,您可以将这些设置用于 HTTP、安全 (SSL)、FTP 和 WAIS 服务,所有服务都使用相同的端口(默认为 3128)。首先,选择“手动代理配置”单选按钮,然后选择“查看”按钮以键入您的设置。图 1 和图 2 显示了这些屏幕的示例。

图 1. 代理首选项屏幕

图 2. 手动代理配置屏幕

另一种解决方案是自动代理配置,它在 Netscape Navigator 3.0 中引入,允许使用多个代理服务器、备份服务器和按域划分的不同服务器。此配置位于一个类似 Javascript 的文件中,必须从服务器检索该文件。使用它,您可以更改缓存网格的拓扑结构或引入必须视为“不使用代理”服务器的新服务器。无需告知用户更改其配置,每次启动浏览器时都会重新加载新的配置文件。自 3.02 版本以来,MS Internet Explorer 也支持自动代理配置功能。

图 3. 自动代理配置屏幕

图 3 显示了 Netscape Navigator 和 Communicator 的此类配置示例。在此示例中,每次启动浏览器时,它都会从服务器 intranet.mec.es 加载文件 proxy.pac。此文件必须以 MIME 类型 application/x-ns-proxy-autoconfig 返回,这可以通过两种方式完成

  1. 或将以下行添加到您的 mime.types 文件

    application/x-ns-proxy-autoconfig pac
  1. 将以下行添加到您的 Apache srm.conf 文件

    AddType application/x-ns-proxy-autoconfig pac

为了使更改生效,您必须将您的代理自动配置文件命名为 .pac 扩展名,并重新启动您的 Web 服务器。Netscape 文档将告诉您有关 .pac 文件的语法(请参阅资源)。尽管如此,我们将看几个关于如何编写它们的基本示例。

Javascript 文件中不应嵌入 HTML 标记,只需包含带有参数 URLhost 的函数 FindProxyForURL。此函数应返回一个包含 DIRECT(直接从源获取对象)或 PROXY host:port(通过此服务器和端口获取对象)的单个字符串。该字符串可以包含多个这些指令,以分号分隔。例如

function FindProxyForURL(
{
return "PROXY proxy1.mec.es:3128;
PROXY proxy2.mec.es:80; DIRECT ";
}

将指示浏览器使用第一个代理来获取对象。如果它无法联系第一个代理 (proxy1),则它将尝试第二个代理 (proxy2);如果两者都关闭,它将从源获取对象。这为我们的缓存系统提供了一定的容错级别。

一个有趣的功能是为不同的域使用不同的代理,并包括对我们不想使用缓存的内部服务器的支持。例如

function FindProxyForURL(
{
 if ( isPlainHostName(host) || dnsDomainIs(host,
        "intranet.mec.es"))
 return "DIRECT";
 else if (shExpMatch(host, "*.com"))
 return "PROXY proxy1.mec.es:3128";
 else
 return "PROXY proxy2.mec.es:80";
}

此函数将直接获取 URL 仅是一个没有点或 Intranet 服务器的单词的所有对象,从 proxy1 获取所有 .COM 对象,从 proxy2 获取其余对象。

作为提示,.pac 文件可以由 CGI 脚本“动态”生成,为不同的浏览器提供不同的代理配置,例如,取决于 CGI 接口提供的 REMOTE_HOST 环境变量。通过这种方式,可以实现不同网络之间的负载均衡。始终记住 CGI 返回的 MIME 类型必须是 application/x-ns-proxy-autoconfig

加入层级结构

如果您的缓存要成为缓存网格的一部分,或者您的代理服务器要连接到另一个将成为其父级的代理,则必须使用 cache_host 指令。您必须为每个邻居包含一行。此行的语法是

cache_peer

其中

  • hostname 是您的邻居的名称。

  • type 是 parent 或 sibling 之一。

  • http_port 是邻居从中获取对象的端口。

  • icp_port 是发送 ICP 查询的端口。如果您的邻居未运行 ICP,则使用值 0;如果您的邻居运行 UDP 回显服务,则使用值 7。这可以帮助 Squid 检测主机是否处于活动状态。

您可以指定选项 default,以便在您无法与父缓存进行 ICP 通信时,将此主机用作最后的手段。另一个选项是 weight=N,以便在邻居选择算法中偏爱特定的父级或同级。值越大,权重越高。

如果您有独立的缓存,则不应包含任何这些指令。如果您有一个父级在其 HTTP 端口 3128 和 ICP 端口 3130 上运行,则要包含在 squid.conf 文件中的行是

cache_peer

使用 cache_peer_domain 指令,您可以限制为特定域查询哪些邻居。例如

cache_peer_domain
cache_peer_domain

将仅为 .COM 和 .EDU 域查询第一个缓存,为某些欧洲域查询第二个缓存。

如果您只有一个父缓存,则 ICP 协议的开销是不必要的。由于您将从父级获取所有对象(命中和未命中),您可以在 cache_peer 指令中使用 no_query 选项,以便仅向该缓存发送 HTTP 查询。

此外,您总是希望直接而不是从邻居那里获取某些域。您自己的域就是一个很好的例子。从遥远的缓存中获取属于您本地 Web 服务器的对象效率不高。在这种情况下,请使用 always_direct acl 命令。例如,在我们的组织中,我们使用

acl intranet dstdomain mec.es
always_direct allow intranet

以避免从国家缓存服务器获取我们自己的对象。

缓存管理器

Squid 包括一个简单的、基于 Web 的界面,称为 cachemgr.cgi,用于监视缓存性能并提供有用的统计信息,例如

  • 正在使用的内存量及其分布方式

  • 文件描述符的数量

  • 它维护的不同缓存的内容(对象、DNS 查找等)

  • 与每个客户端和邻居的流量统计

  • “利用率”页面,您可以在其中检查您的缓存正在注册的命中百分比(以及您正在节省的带宽)。

确保将安装在您的 /usr/local/squid/bin(或您选择的任何位置)中的 cachemgr.cgi 程序复制到您的标准 CGI 目录,并将您的浏览器指向 http://your.cache.host/cgi-bin/cachemgr.cgi。在那里,您应该键入您的缓存主机名,通常为“localhost”或您的系统名称,以及您的缓存正在运行的端口,通常为 3128,并检查所有选项。

结论和提示

对于几乎任何连接到 Internet 的组织来说,代理缓存服务器都是一项必要的服务。在本文中,我们试图展示实施这项技术的理由和方法,以及关于 Squid 的简要教程,Squid 是用于此目的的最先进和最强大的工具。不要忘记阅读示例配置文件中的所有注释。它们完整且有用,并展示了本文中未提及的许多功能。

也许在几年后,随着 PUSH 技术的增长和 Web 上动态内容的使用,缓存将不再是解决带宽危机的方案。但今天,它是我们拥有的最好的方案。

代理缓存无法解决的一个问题是确保您的用户配置其浏览器以使用缓存。用户始终可以选择不配置其浏览器来绕过您的代理服务器。一些组织已选择阻止其路由器中的端口 80,除了运行代理缓存服务器的系统。这是一个激进的解决方案,但非常有效。

您可以做的另一件事来提高用户浏览器的速度是预取缓存中访问最多的网站。支持代理连接的递归 Web 获取工具可以帮助在非高峰时段执行此任务,例如 url_getwebcopy。启动其中一个检索工具并将标准输出重定向到 /dev/null 会使用新的对象更新缓存。

资源

Caching the Web, Part 2
David Guerrero 是 Boletin Oficial del Estado 的系统和网络管理员。自 .98plNN 时代以来,他一直在使用 Linux,现在正在玩一些 Alpha-Linux 机器。当不工作或学习时,他喜欢与他的爱人 Yolanda 共度时光,旅行,弹吉他和合成器,或者和他的“colegas”出去玩。可以通过 david@boe.es 联系到他。
加载 Disqus 评论