第8章 高级磁盘缓存主题
8.1 是否存在磁盘I/O瓶颈?
Web缓存器例如squid,通常在磁盘I/O变成瓶颈时,不会正确的体现和告知你。代替的是,随着负载的增加,响应时间和/或命中率会更低效。当然,响应时间和命中率可能因为其他原因而改变,例如网络延时和客户请求方式的改变。
也许探测cache性能瓶颈的最好方式是做压力测试,例如Web Polygraph。压力测试的前提是你能完全控制环境,消除未知因素。你可以用不同的cache配置来重复相同的测试。不幸的是,压力测试通常需要大量的时间,并要求有空闲的系统(也许它们正在使用中)。
假如你有资源执行squid压力测试,请以标准的cache工作负载开始。当你增加负载时,在某些点上你能看到明显的响应延时和/或命中率下降。一旦你观 察到这样的性能降低,就禁止掉磁盘缓存,再测试一次。你可以配置squid从来不缓存任何响应(使用null存储机制,见8.7章)。代替的,你能配置工 作负载到100%不可cache响应。假如不使用cache时,平均响应时间明显更好,那么可以确认磁盘I/O是该水平吞吐量的瓶颈。 假如你没有时间或没有资源来执行squid压力测试,那么可检查squid的运行时统计来查找磁盘I/O瓶颈。cache管理器的General Runtime Information 页面(见14章)会显示出cache命中和cache丢失的中值响应时间。 Median Service Times (seconds) 5 min 60 min: HTTP Requests (All): 0.39928 0.35832 Cache Misses: 0.42149 0.39928 Cache Hits: 0.12783 0.11465 Near Hits: 0.37825 0.39928 Not-Modified Replies: 0.07825 0.07409
对健壮的squid缓存来说,命中显然快于丢失。中值命中响应时间典型的少于0.5秒或更少。我强烈建议你使用SNMP或其他的网络监视工具来从squid缓存采集定期测量值。如果平均命中响应时间增加得太明显,意味着系统有磁盘I/0瓶颈。
假如你认为产品cache面临此类问题,可以用前面提到的同样的技术来验证你的推测。配置squid不cache任何响应,这样就避开了所有磁盘I/O。然后仔细观察cache丢失响应时间。假如它降下去,那么你的推测该是正确的。
一旦你确认了磁盘吞吐能力是squid的性能瓶颈,那么可做许多事来改进它。其中一些方法要求重编译squid,然而另一些相对较简单,只需调整Unix文件系统。
8.2 文件系统调整选项
首先,从来不在squid的缓存目录中使用RAID。以我的经验看,RAID总是降低squid使用的文件系统的性能。最好有许多独立的文件系统,每个文件系统使用单独的磁盘驱动器。
我发现4个简单的方法来改进squid的UFS性能。其中某些特指某种类型的操作系统例如BSD和Linux,也许对你的平台不太合适:
1.某些UFS支持一个noatime的mount选项。使用noatime选项来mount的文件系统,不会在读取时,更新相应的i节点访问时间。使用该选项的最容易的方法是在/etc/fstab里增加如下行: # Device Mountpoint FStype Options Dump Pass# /dev/ad1s1c /cache0 ufs rw,noatime 0 0
2.检查mount(的manpage 里的async选项。设置了该选项,特定的I/O操作(例如更新目录)会异步执行。某些系统的文档会标明这是个危险的标签。某天你的系统崩溃,你也许会丢 失整个文件系统。对许多squid安装来说,执行性能的提高值得冒此风险。假如你不介意丢失整个cache内容,那么可以使用该选项。假如cache数据 非常有价值,async选项也许不适合你。
3.BSD有一个功能叫做软更新。软更新是BSD用于Journaling文件系统的代替品。在FreeBSD上,你可以在没有mount的文件系统中,使用tunefs命令来激活该选项: # umount /cache0 # tunefs -n enable /cache0 # mount /cache0
4.你对每个文件系统运行一次tunefs命令就可以了。在系统重启时,软更新自动在文件系统中激活了。 在OpenBSD和NetBSD中,可使用softdep mount选项: # Device Mountpoint FStype Options Dump Pass# /dev/sd0f /usr ffs rw,softdep 1 2
假如你象我一样,你可能想知道在async选项和软更新选项之间有何不同。一个重要的区别是,软更新代码被设计成在系统崩溃事件中,保持文件系统的一致性,而async选项不是这样的。这也许让你推断async执行性能好于软更新。然而,如我在附录D中指出的,事实相反。
以前我提到过,UFS性能特别是写性能,依赖于空闲磁盘的数量。对空文件系统的磁盘写操作,要比满文件系统快得多。这是UFS的最小自由空间参 数,和空间/时间优化权衡参数背后的理由之一。假如cache磁盘满了,squid执行性能看起来很糟,那么试着减少cache_dir的容量值,以便更 多的自由空间可用。当然,减少cache大小也会降低命中率,但响应时间的改进也许值得这么做。假如你给squid缓存配置新的设备,请考虑使用超过你需 要的更大磁盘,并且仅仅使用空间的一半。
8.3 可选择的文件系统
某些操作系统支持不同于UFS(或ext2fs)的文件系统。Journaling文件系统是较普遍的选择。在UFS和Journaling文件 系统之间的主要不同在于它们处理更新的方式。在UFS下,更新是实时的。例如,当你改变了某个文件并且将它存储到磁盘,新数据就替换了旧数据。当你删除文 件时,UFS直接更新了目录。
Journaling文件系统与之相反,它将更新写往独立的记帐系统,或日志文件。典型的你能选择是否记录文件改变或元数据改变,或两者兼备。某 个后台进程在空闲时刻读取记帐,并且执行实际的改变操作。Journaling文件系统典型的在系统崩溃后比UFS恢复更快。在系统崩溃后, Journaling文件系统简单的读取记帐,并且提交所有显著的改变。
Journaling文件系统的主要弊端在于它们需要额外的磁盘写操作。改变首先写往日志文件,然后才写往实际的文件或目录。这对web缓存影响尤其明显,因为首先web缓存倾向于更多的磁盘写操作。
Journaling文件系统对许多操作系统可用。在Linux上,你能选择ext3fs,reiserfs, XFS,和其他的。XFS也可用在SGI/IRIX,它原始是在这里开发的。Solaris用户能使用Veritas文件系统产品。TRU64(以前的 Digital Unix)高级文件系统(advfs)支持Journaling。 你可以不改变squid的任何配置而使用Journaling文件系统。简单的创建和挂载在操作系统文档里描述的文件系统,而不必改变squid.cf配置文件里的cache_dir行。
用类似如下命令在Linux中制作reiserfs文件系统: # /sbin/mkreiserfs /dev/sda2
对XFS,使用: # mkfs -t xfs -f /dev/sda2
注意ext3fs其实简单的就是激活了记帐的ext2fs。当创建该文件系统时,对mke2fs使用-j选项: # /sbin/mke2fs -j /dev/sda2
请参考其他操作系统的相关文档。
8.4 aufs存储机制
aufs存储机制已经发展到超出了改进squid磁盘I/O响应时间的最初尝试。"a"代表着异步I/O。默认的ufs和aufs之间的唯一区别,在于I/O是否被squid主进程执行。数据格式都是一样的,所以你能在两者之间轻松选择,而不用丢失任何cache数据。
aufs使用大量线程进行磁盘I/O操作。每次squid需要读写,打开关闭,或删除cache文件时,I/O请求被分派到这些线程之一。当线程 完成了I/O后,它给squid主进程发送信号,并且返回一个状态码。实际上在squid2.5中,某些文件操作默认不是异步执行的。最明显的,磁盘写总 是同步执行。你可以修改src/fs/aufs/store_asyncufs.h文件,将ASYNC_WRITE设为1,并且重编译squid。
aufs代码需要pthreads库。这是POSIX定义的标准线程接口。尽管许多Unix系统支持pthreads库,但我经常遇到兼容性问 题。aufs存储系统看起来仅仅在Linux和Solaris上运行良好。在其他操作系统上,尽管代码能编译,但也许会面临严重的问题。
为了使用aufs,可以在./configure时增加一个选项: % ./configure --enable-storeio=aufs,ufs
严格讲,你不必在storeio模块列表中指定ufs。然而,假如你以后不喜欢aufs,那么就需要指定ufs,以便能重新使用稳定的ufs存储机制。 假如愿意,你也能使用—with-aio-threads=N选项。假如你忽略它,squid基于aufs cache_dir的数量,自动计算可使用的线程数量。表8-1显示了1-6个cache目录的默认线程数量。 Table 8-1. Default number of threads for up to six cache directories cache_dirs Threads 1 16 2 26 3 32 4 36 5 40 6 44
将aufs支持编译进squid后,你能在squid.conf文件里的cache_dir行后指定它: cache_dir aufs /cache0 4096 16 256
在激活了aufs并启动squid后,请确认每件事仍能工作正常。可以运行tail -f store.log一会儿,以确认缓存目标被交换到磁盘。也可以运行tail -f cache.log并且观察任何新的错误或警告。
8.4.1 aufs如何工作
Squid通过调用pthread_create()来创建大量的线程。所有线程在任何磁盘活动之上创建。这样,即使squid空闲,你也能见到所有的线程。 无论何时,squid想执行某些磁盘I/O操作(例如打开文件读),它分配一对数据结构,并将I/O请求放进队列中。线程循环读取队列,取得I/O请求并执行它们。因为请求队列共享给所有线程,squid使用独享锁来保证仅仅一个线程能在给定时间内更新队列。
I/O操作阻塞线程直到它们被完成。然后,将操作状态放进一个完成队列里。作为完整的操作,squid主进程周期性的检查完成队列。请求磁盘I/O的模块被通知操作已完成,并获取结果。
你可能已猜想到,aufs在多CPU系统上优势更明显。唯一的锁操作发生在请求和结果队列。然而,所有其他的函数执行都是独立的。当主进程在一个CPU上执行时,其他的CPU处理实际的I/O系统调用。
8.4.2 aufs发行 线程的有趣特性是所有线程共享相同的资源,包括内存和文件描述符。例如,某个线程打开一个文件,文件描述符为27,所有其他线程能以相同的文件描 述符来访问该文件。可能你已知道,在初次管理squid时,文件描述符短缺是较普遍问题。Unix内核典型的有两种文件描述符限制: 进程级的限制和系统级的限制。你也许认为每个进程拥有256个文件描述符足够了(因为使用线程),然而并非如此。在这样的情况下,所有线程共享少量的文件描述符。请确认增加系统的进程文件描述符限制到4096或更高,特别在使用aufs时。
调整线程数量有点棘手。在某些情况下,可在cache.log里见到如下警告: 2003/09/29 13:42:47| squidaio_queue_request: WARNING - Disk I/O overloading
这意味着squid有大量的I/O操作请求充满队列,等待着可用的线程。你首先会想到增加线程数量,然而我建议,你该减少线程数量。
增加线程数量也会增加队列的大小。超过一定数量,它不会改进aufs的负载能力。它仅仅意味着更多的操作变成队列。太长的队列导致响应时间变长,这绝不是你想要的。
减少线程数量和队列大小,意味着squid检测负载条件更快。当某个cache_dir超载,它会从选择算法里移除掉(见7.4章)。然后, squid选择其他的cache_dir或简单的不存储响应到磁盘。这可能是较好的解决方法。尽管命中率下降,响应时间却保持相对较低。
8.4.3 监视aufs操作
Cache管理器菜单里的Async IO Counters选项,可以显示涉及到aufs的统计信息。它显示打开,关闭,读写,stat,和删除接受到的请求的数量。例如: % squidclient mgr:squidaio_counts ... ASYNC IO Counters: Operation # Requests open 15318822 close 15318813 cancel 15318813 write 0 read 19237139 stat 0 unlink 2484325 check_callback 311678364 queue 0
取消(cancel)计数器正常情况下等同于关闭(close)计数器。这是因为close函数总是调用cancel函数,以确认任何未决的I/O操作被忽略。 写(write)计数器为0,因为该版本的squid执行同步写操作,即使是aufs。
check_callbak计数器显示squid主进程对完成队列检查了多少次。
queue值显示当前请求队列的长度。正常情况下,队列长度少于线程数量的5倍。假如你持续观察到队列长度大于这个值,说明squid配得有问题。增加更多的线程也许有帮助,但仅仅在特定范围内。
8.5 diskd存储机制
diskd(disk守护进程的短称)类似于aufs,磁盘I/O被外部进程来执行。不同于aufs的是,diskd不使用线程。代替的,它通过消息队列和共享内存来实现内部进程间通信。
消息队列是现代Unix操作系统的标准功能。许多年以前在AT&T的Unix System V的版本1上实现了它们。进程间的队列消息以较少的字节传递:32-40字节。每个diskd进程使用一个队列来接受来自squid的请求,并使用另一个队列来传回请求。
8.5.1 diskd如何工作
Squid对每个cache_dir创建一个diskd进程。这不同于aufs,aufs对所有的cache_dir使用一个大的线程池。对每个 I/O操作,squid发送消息到相应的diskd进程。当该操作完成后,diskd进程返回一个状态消息给squid。squid和diskd进程维护 队列里的消息的顺序。这样,不必担心I/O会无序执行。
对读和写操作,squid和diskd进程使用共享内存区域。两个进程能对同一内存区域进行读和写。例如,当squid产生读请求时,它告诉 diskd进程在内存中何处放置数据。diskd将内存位置传递给read()系统调用,并且通过发送队列消息,通知squid该过程完成了。然后 squid从共享内存区域访问最近的可读数据。
diskd与aufs本质上都支持squid的无阻塞磁盘I/O。当diskd进程在I/O操作上阻塞时,squid有空去处理其他任务。在 diskd进程能跟上负载情况下,这点确实工作良好。因为squid主进程现在能够去做更多工作,当然它有可能会加大diskd的负载。diskd有两个 功能来帮助解决这个问题。 首先,squid等待diskd进程捕获是否队列超出了某种极限。默认值是64个排队消息。假如diskd进程获取的数值远大于此,squid 会休眠片刻,并等待diskd完成一些未决操作。这本质上让squid进入阻塞I/O模式。它也让更多的CPU时间对diskd进程可用。通过指定 cache_dir行的Q2参数的值,你可以配置这个极限值: cache_dir diskd /cache0 7000 16 256 Q2=50
第二,假如排队操作的数量抵达了另一个极限,squid会停止要求diskd进程打开文件。这里的默认值是72个消息。假如squid想打开一个 磁盘文件读或写,但选中的cache_dir有太多的未完成操作,那么打开请求会失败。当打开文件读时,会导致cache丢失。当打开文件写时,会阻碍 squid存储cache响应。这两种情况下用户仍能接受到有效响应。唯一实际的影响是squid的命中率下降。这个极限用Q1参数来配置: cache_dir diskd /cache0 7000 16 256 Q1=60 Q2=50
注意在某些版本的squid中,Q1和Q2参数混杂在默认的配置文件里。最佳选择是,Q1应该大于Q2。
8.5.2 编译和配置diskd
为了使用diskd,你必须在运行./configure时,在--enable-storeio列表后增加一项: % ./configure --enable-storeio=ufs,diskd
diskd看起来是可移植的,既然共享内存和消息队列在现代Unix系统上被广泛支持。然而,你可能需要调整与这两者相关的内核限制。内核典型的有如下可用参数:
MSGMNB 每个消息队列的最大字节限制。对diskd的实际限制是每个队列大约100个排队消息。squid传送的消息是32-40字节,依赖于你的CPU体系。这样,MSGMNB应该是4000或更多。为安全起见,我推荐设置到8192。
MSGMNI 整个系统的最大数量的消息队列。squid对每个cache_dir使用两个队列。假如你有10个磁盘,那就有20个队列。你也许该增加更多,因为其他应用程序也要使用消息队列。我推荐的值是40。
MSGGSZ 消息片断的大小(字节)。大于该值的消息被分割成多个片断。我通常将这个值设为64,以使diskd消息不被分割成多个片断。
MSGSEG 在单个队列里能存在的最大数量的消息片断。squid正常情况下,限制队列的长度为100个排队消息。记住,在64位系统中,假如你没有增加MSGSSZ的值到64,那么每个消息就会被分割成不止1个片断。为了安全起见,我推荐设置该值到512。
MSGTQL 整个系统的最大数量的消息。至少是cache_dir数量的100倍。在10个cache目录情况下,我推荐设置到2048。
MSGMAX 单个消息的最大size。对Squid来说,64字节足够了。然而,你系统中的其他应用程序可能要用到更大的消息。在某些操作系统例如BSD中, 你不必设置这个。BSD自动设置它为MSGSSZ * MSGSEG。其他操作系统中,你也许需要改变这个参数的默认值,你可以设置它与MSGMNB相同。
SHMSEG 每个进程的最大数量的共享内存片断。squid对每个cache_dir使用1个共享内存标签。我推荐设置到16或更高。
SHMMNI 共享内存片断数量的系统级的限制。大多数情况下,值为40足够了。
SHMMAX 单个共享内存片断的最大size。默认的,squid对每个片断使用大约409600字节。 为安全起见,我推荐设置到2MB,或2097152。
SHMALL 可分配的共享内存数量的系统级限制。在某些系统上,SHMALL可能表示成页数量,而不是字节数量。在10个cache_dir的系统上,设置该值到16MB(4096页)足够了,并有足够的保留给其他应用程序。
在BSD上配置消息队列,增加下列选项到内核配置文件里:
# System V message queues and tunable parameters options SYSVMSG # include support for message queues options MSGMNB=8192 # max characters per message queue options MSGMNI=40 # max number of message queue identifiers options MSGSEG=512 # max number of message segments per queue options MSGSSZ=64 # size of a message segment MUST be power of 2 options MSGTQL=2048 # max number of messages in the system options SYSVSHM options SHMSEG=16 # max shared mem segments per process options SHMMNI=32 # max shared mem segments in the system options SHMMAX=2097152 # max size of a shared mem segment options SHMALL=4096 # max size of all shared memory (pages)
在Linux上配置消息队列,增加下列行到/etc/sysctl.conf:
kernel.msgmnb=8192 kernel.msgmni=40 kernel.msgmax=8192 kernel.shmall=2097152 kernel.shmmni=32 kernel.shmmax=16777216
另外,假如你需要更多的控制,可以手工编辑内核资源文件中的include/linux/msg.h和include/linux/shm.h。
在Solaris上,增加下列行到/etc/system,然后重启:
set msgsys:msginfo_msgmax=8192 set msgsys:msginfo_msgmnb=8192 set msgsys:msginfo_msgmni=40 set msgsys:msginfo_msgssz=64 set msgsys:msginfo_msgtql=2048 set shmsys:shminfo_shmmax=2097152 set shmsys:shminfo_shmmni=32 set shmsys:shminfo_shmseg=16
在Digital Unix(TRU64)上,可以增加相应行到BSD风格的内核配置文件中,见前面所叙。另外,你可使用sysconfig命令。首先,创建如下的ipc.stanza文件:
ipc: msg-max = 2048 msg-mni = 40 msg-tql = 2048 msg-mnb = 8192 shm-seg = 16 shm-mni = 32 shm-max = 2097152 shm-max = 4096
然后,运行这个命令并重启: # sysconfigdb -a -f ipc.stanza
一旦你在操作系统中配置了消息队列和共享内存,就可以在squid.conf里增加如下的cache_dir行: cache_dir diskd /cache0 7000 16 256 Q1=72 Q2=64 cache_dir diskd /cache1 7000 16 256 Q1=72 Q2=64 ...
8.5.3 监视diskd
监视diskd运行的最好方法是使用cache管理器。请求diskd页面,例如:
% squidclient mgr:diskd ... sent_count: 755627 recv_count: 755627 max_away: 14 max_shmuse: 14 open_fail_queue_len: 0 block_queue_len: 0
OPS SUCCESS FAIL open 51534 51530 4 create 67232 67232 0 close 118762 118762 0 unlink 56527 56526 1 read 98157 98153 0 write 363415 363415 0
请见14.2.1.6章关于该输出的详细描述。
8.6 coss存储机制
循环目标存储机制(Cyclic Object Storage Scheme,coss)尝试为squid定制一个新的文件系统。在ufs基础的机制下,主要的性能瓶颈来自频繁的open()和unlink()系统调 用。因为每个cache响应都存储在独立的磁盘文件里,squid总是在打开,关闭,和删除文件。
与之相反的是,coss使用1个大文件来存储所有响应。在这种情形下,它是特定供squid使用的,小的定制文件系统。coss实现许多底层文件系统的正常功能,例如给新数据分配空间,记忆何处有自由空间等。
不幸的是,coss仍没开发完善。coss的开发在过去数年里进展缓慢。虽然如此,基于有人喜欢冒险的事实,我还是在这里描述它。
8.6.1 coss如何工作
在磁盘上,每个coss cache_dir是一个大文件。文件大小一直增加,直到抵达它的大小上限。这样,squid从文件的开头处开始,覆盖掉任何存储在这里的数据。然后,新的目标总是存储在该文件的末尾处。
squid实际上并不立刻写新的目标数据到磁盘上。代替的,数据被拷贝进1MB的内存缓冲区,叫做stripe。在stripe变满后,它被写往磁盘。coss使用异步写操作,以便squid主进程不会在磁盘I/O上阻塞。
象其他文件系统一样,coss也使用块大小概念。在7.1.4章里,我谈到了文件号码。每个cache目标有一个文件号码,以便squid用于定 位磁盘中的数据。对coss来说,文件号码与块号码一样。例如,某个cache目标,其交换文件号码等于112,那它在coss文件系统中就从第112块 开始。因此coss不分配文件号码。某些文件号码不可用,因为cache目标通常在coss文件里占用了不止一个块。
coss块大小在cache_dir选项中配置。因为squid的文件号码仅仅24位,块大小决定了coss缓存目录的最大size:size = 块大小 x (2的24次方)。例如,对512字节的块大小,你能在coss cache_dir中存储8GB数据。
coss不执行任何squid正常的cache置换算法(见7.5章)。代替的,cache命中被"移动"到循环文件的末尾。这本质上是LRU算法。不幸的是,它确实意味着cache命中导致磁盘写操作,虽然是间接的。
在coss中,没必要去删除cache目标。squid简单的忘记无用目标所分配的空间。当循环文件的终点再次抵达该空间时,它就被重新利用。
8.6.2 编译和配置coss
为了使用coss,你必须在运行./configure时,在--enable-storeio列表里增加它: % ./configure --enable-storeio=ufs,coss ...
coss缓存目录要求max-size选项。它的值必须少于stripe大小(默认1MB,但可以用--enable-coss-membuf-size选项来配置)。也请注意你必须忽略L1和L2的值,它们被ufs基础的文件系统使用。如下是示例:
cache_dir coss /cache0/coss 7000 max-size=1000000 cache_dir coss /cache1/coss 7000 max-size=1000000 cache_dir coss /cache2/coss 7000 max-size=1000000 cache_dir coss /cache3/coss 7000 max-size=1000000 cache_dir coss /cache4/coss 7000 max-size=1000000
甚至,你可以使用block-size选项来改变默认的coss块大小。 cache_dir coss /cache0/coss 30000 max-size=1000000 block-size=2048
关于coss的棘手的事情是,cache_dir目录参数(例如/cache0/coss)实际上不是目录,它是squid打开或创建的常规文件。所以你可以用裸设备作为coss文件。假如你错误的创建coss文件作为目录,你可以在squid启动时见到如下错误:
2003/09/29 18:51:42| /usr/local/squid/var/cache: (21) Is a directory FATAL: storeCossDirInit: Failed to open a coss file.
因为cache_dir参数不是目录,你必须使用cache_swap_log指令(见13.6章)。否则squid试图在cache_dir目录中创建swap.state文件。在该情形下,你可以见到这样的错误:
2003/09/29 18:53:38| /usr/local/squid/var/cache/coss/swap.state: (2) No such file or directory FATAL: storeCossDirOpenSwapLog: Failed to open swap log.
coss使用异步I/O以实现更好的性能。实际上,它使用aio_read()和aio_write()系统调用。这点也许并非在所有操作系统中 可用。当前它们可用在FreeBSD,Solaris,和Linux中。假如coss代码看起来编译正常,但你得到"Function not implemented"错误消息,那就必须在内核里激活这些系统调用。在FreeBSD上,必须在内核配置文件中有如下选项: options VFS_AIO
8.6.3 coss发行
coss还是实验性的功能。没有充分证实源代码在日常使用中的稳定性。假如你想试验一下,请做好存储在coss cache_dir中的资料丢失的准备。 从另一面说,coss的初步性能测试表现非常优秀。示例请见附录D。
coss没有很好的支持从磁盘重建cache数据。当你重启squid时,你也许会发现从swap.state文件读取数据失败,这样就丢失了所有的缓存数据。甚至,squid在重启后,不能记忆它在循环文件里的位置。它总是从头开始。
coss对目标置换采用非标准的方式。相对其他存储机制来说,这可能导致命中率更低。
某些操作系统在单个文件大于2GB时,会有问题。假如这样的事发生,你可以创建更多小的coss区域。例如: cache_dir coss /cache0/coss0 1900 max-size=1000000 block-size=128 cache_dir coss /cache0/coss1 1900 max-size=1000000 block-size=128 cache_dir coss /cache0/coss2 1900 max-size=1000000 block-size=128 cache_dir coss /cache0/coss3 1900 max-size=1000000 block-size=128
使用裸磁盘设备(例如/dev/da0s1c)也不会工作得很好。理由之一是磁盘设备通常要求I/O发生在512个字节的块边界(译者注:也就是块设备访问)。另外直接的磁盘访问绕过了系统高速缓存,可能会降低性能。然而,今天的许多磁盘驱动器,已经内建了高速缓存。
8.7 null存储机制 Squid有第5种存储机制叫做null。就像名字暗示的一样,这是最不健壮的机制。写往null cache_dir的文件实际上不被写往磁盘。 大多数人没有任何理由要使用null存储系统。当你想完全禁止squid的磁盘缓存时,null才有用。你不能简单的从squid.conf文件 里删除所有cache_dir行,因为这样的话squid会增加默认的ufs cache_dir。null存储系统有些时候在测试squid,和压力测试时有用。既然文件系统是典型的性能瓶颈,使用null存储机制能获取基于当前 硬件的squid的性能上限。
为了使用该机制,在运行./configure时,你必须首先在--enable-storeio列表里指定它: % ./configure --enable-storeio=ufs,null ...
然后在squid.conf里创建cache_dir类型为null: cache_dir /tmp null
也许看起来有点奇怪,你必须指定目录给null存储机制。squid使用目录名字作为cache_dir标识符。例如,你能在cache管理器的输出里见到它。 8.8 哪种最适合我?
Squid的存储机制选择看起来有点混乱和迷惑。aufs比diskd更好?我的系统支持aufs或coss吗?假如我使用新的机制,会丢失数据吗?可否混合使用存储机制?
首先,假如Squid轻度使用(就是说每秒的请求数少于5个),默认的ufs存储机制足够了。在这样的低请求率中,使用其他存储机制,你不会观察到明显的性能改进。
假如你想决定何种机制值得一试,那你的操作系统可能是个决定因素。例如,aufs在Linux和Solaris上运行良好,但看起来在其他系统中有问题。另外,coss代码所用到的函数,当前不可用在某些操作系统中(例如NetBSD)。
从我的观点看来,高性能的存储机制在系统崩溃事件中,更易受数据丢失的影响。这就是追求最好性能的权衡点。然而对大多数人来说,cache数据相 对价值较低。假如squid的缓存因为系统崩溃而破坏掉,你会发现这很容易,只需简单的newfs磁盘分区,让cache重新填满即可。如果你觉得替换 Squid的缓存内容较困难或代价很大,你就应该使用低速的,但可信的文件系统和存储机制。
近期的Squid允许你对每个cache_dir使用不同的文件系统和存储机制。然而实际上,这种做法是少见的。假如所有的cache_dir使用相同的size和相同的存储机制,可能冲突更少。 第9章 Cache拦截
Cache拦截是让传输流向Squid的流行技术,它不用配置任何客户端。你可以配置路由器或交换机将HTTP连接转发到squid运行的主机。 squid运行的操作系统被配置成接受外部数据包,并将其递交给squid进程。为了让HTTP拦截生效,你必须配置3个独立的因素:网络设备, squid运行的操作系统,和squid自身。
(译者注:Cache拦截实际上指的是Squid的透明代理)
9.1它如何工作?
Cache拦截包含了某些网络欺骗,它对理解在客户端和Squid之间的会话有用。我使用图9-1和如下的tcpdump示例输出,来解释当数据包通过网络时,如何被拦截。
1.用户代理(user-agent)想请求某个资源,它对原始服务器发起index.html请求,例如:。它需要原始服务器的IP地址,所以先发起一个DNS请求:
Packet 1 TIME: 19:54:41.317310 UDP: 206.168.0.3.2459 -> 206.168.0.2.53 DATA: .d........... ---------------------------------------------------------------------------
Packet 2 TIME: 19:54:41.317707 (0.000397) UDP: 206.168.0.2.53 -> 206.168.0.3.2459 DATA: .d...................PR.....%........PR. ....$........PR...ns1.sonic.net.........PR...ns2.Q........PR ...ns...M...............h.............!.z.......b......
2.现在有了IP地址,用户代理初始化到原始服务器80端口的TCP连接:
Packet 3 TIME: 19:54:41.320652 (0.002945) TCP: 206.168.0.3.3897 -> 208.201.239.37.80 Syn DATA:
3.路由器或交换机注意到目的地址是80端口的TCP SYN包。下一步会发生什么依赖于特定的拦截技术。在4层交换和路由策略上,网络设备简单的将TCP包转发到Squid的数据链路地址。当squid直接 挂在网络设备上时,就这样工作。对WCCP来说,路由器封装TCP包为GRE包。因为GRE包有它自己的IP地址,它可能被通过多个子网进行路由。换句话 说,WCCP不要求squid直接挂在路由器上。
4.Squid主机的操作系统接受到拦截包。对4层交换来说,TCP/IP包并没有改变。
假如包使用了GRE封装,主机会剥离外部的IP和GRE头部,并将原始的TCP/IP包放在输入队列里。
注意squid主机接受到的包是针对外部地址的(原始服务器的)。正常情况下,这个包不匹配任何本地地址,它会被丢弃。为了让主机接受外部数据包,你必须在大多数操作系统上激活IP转发。
5.客户端的TCP/IP包被包过滤代码处理。数据包必须匹配某个规则,该规则指示内核转交这个包给squid。如果没有这样的规则,内核简单的将包按照它自己的方式转发给原始服务器,这不是你想要的。
注意SYN包的目的端口是80,但squid可能侦听在不同的端口,例如3128。包过滤规则允许你改变端口号。你不必让squid侦听在80端口。通过tcpdump,你能见到这步,因为转发的包不会再次通过网络接口代码。
即使squid侦听在80端口,包过滤器的重定向规则仍是必要的。可以让squid不在这些端口上接受拦截包。重定向规则有点神奇,它转交外部数据包给squid。
6.Squid接受到新连接的通知,它接受这个连接。内核发送SYN/ACK包返回给客户端:
Packet 4 TIME: 19:54:41.320735 (0.000083) TCP: 208.201.239.37.80 -> 206.168.0.3.3897 SynAck DATA:
就象你见到的一样,源地址是原始服务器,尽管这个包不会抵达原始服务器。操作系统只是简单的将源地址和目的地址交换一下,并将它放进响应数据包里。
7.用户代理接受到SYN/ACK包,建立起完整的TCP连接。用户代理现在相信它是连接到原始服务器,所以它发送HTTP请求:
Packet 5 TIME: 19:54:41.323080 (0.002345) TCP: 206.168.0.3.3897 -> 208.201.239.37.80 Ack DATA: --------------------------------------------------------------------------- Packet 6 TIME: 19:54:41.323482 (0.000402) TCP: 206.168.0.3.3897 -> 208.201.239.37.80 AckPsh DATA: GET / HTTP/1.0 User-Agent: Wget/1.8.2 Host: Accept: */* Connection: Keep-Alive
8.Squid接受HTTP请求。它使用HTTP Host头部来转换局部URL为完整的URL。在这种情形下,可在access.log文件里见到。
9.从这点开始,squid正常的处理请求。一般cache命中会立刻返回。cache丢失会转发到原始服务器。
10.最后,是squid从原始服务器接受到的响应:
Packet 8 TIME: 19:54:41.448391 (0.030030) TCP: 208.201.239.37.80 -> 206.168.0.3.3897 AckPsh DATA: HTTP/1.0 200 OK Date: Mon, 29 Sep 2003 01:54:41 GMT Server: Apache/1.3.26 (Unix) PHP/4.2.1 mod_gzip/1.3.19.1a mo d_perl/1.27 P3P: policyref=" AO DSP COR CURa ADMa DEVa TAIa PSAa PSDa IVAa IVDa CONo OUR DELa PUBi OTRa IND PHY ONL UNI PUR COM NAV INT DEM CNT STA P RE" Last-Modified: Sun, 28 Sep 2003 23:54:44 GMT ETag: "1b76bf-b910-3ede86c4" Accept-Ranges: bytes Content-Length: 47376 Content-Type: text/html X-Cache: MISS from X-Cache: MISS from 10.0.0.1 Connection: keep-alive
不应该让交换机或路由器来拦截squid到原始服务器的连接。假如这种情况发生,squid结束与自己的会话,并且不能满足任何cache丢失。 防止这类转发死循环的最好方法是,确认用户和squid连接到交换机或路由器的独立接口。无论何时,应该在指定接口上应用拦截规则。最明显的,不该在 squid使用的接口上激活拦截。
9.2为何要(或不要)拦截?
许多单位发现,cache拦截很有用,因为他们不能,或不愿意配置所有用户的web浏览器。相对于配置成百上千台工作站来说,在单个交换机或路由器上做一点网络欺骗更容易。从我们面临的许多选择来看,cache拦截确实有好也有坏。它可能让你的生活更容易,但也许会更难。
Cache拦截的最明显的贡献是,所有HTTP请求通过squid自动离开你的网络。你不必担心配置任何浏览器,用户可能在浏览器上禁止他们的代 理设置。cache拦截让网络管理员完全控制HTTP会话。你可以改变,增加,或删除squid的缓存,而不会显著影响你的用户上网冲浪。
关于HTTP拦截的主要不利点就是该技术违背了TCP/IP的标准。这些协议要求路由器或交换机转发TCP/IP包到目的IP地址里指定的主机。 然而转发包到cache代理破坏了这些规则。代理伪装身份接受转交过来的连接。用户代理被欺骗了,以为它们在与真正的web服务器会话。
这样的混乱导致在老版本的Microsoft IE浏览器中产生严重问题。浏览器的Reload按钮是刷新HTML页面的最容易的方法。当浏览器被配置成使用cache代理时,reload请求包含了 一个Cache-Control:no-cache头部,它强迫产生cache丢失(或cache确认),并确保响应是最近更新的。假如没有明确配置使用 代理,浏览器会忽略该头部。当使用cache拦截时,浏览器认为它在连接到原始服务器,因此没必要发送该头部。在这种情形下,squid不会告知用户的 Reload按钮,也许不会验证cache响应。squid的ie_refresh提供了解决此bug的局部解决方法(见附录A)。Microsoft已 经在其IE 5.5 SP1中解决了这个问题。
因为类似的理由,你不能结合cache拦截使用HTTP代理验证。因为客户端不知道这个代理,它不会发送必要的Proxy- Authorization头部。另外,407(代理验证请求)响应代码也不恰当,因为响应看起来象来自原始服务器,原始服务器从来不会发送如此响应。
也不能在cache拦截中使用RFC 1413 ident查询(见6.1.2.11章节)。Squid不能对必要的IP地址建立新的TCP Socket连接。操作系统在转发拦截连接到squid时,它执行欺骗。然后,当squid希望bind新的TCP Socket到外部IP地址时,它不能执行欺骗。它想bind的地址实际上并非真正本地的,所以bind系统调用失败。
cache拦截也与设计成阻止地址欺骗的IP过滤冲突(见RFC 2267:Network Ingress Filtering: Defeating Denial of Service Attacks Which Employ IP Source AddressSpoofing)。考虑如图9-2显示的网络。路由器有2个LAN接口:lan0和lan1。网络管理员在路由器上使用包过滤器,以确保 没有内部主机传送假冒源地址的数据包。路由器只会转发源地址对应相连网络的数据包。包过滤规则也许看起来如下:
# lan0 allow ip from 172.16.1.0/24 to any via lan0 deny ip from any to any via lan0
# lan1 allow ip from 10.0.0.0/16 to any via lan1 deny ip from any to any via lan1
现在看看,当路由器和lan1中的squid主机配置成拦截来自lan0中的HTTP连接后,会发生什么。Squid装扮成原始服务器,这意味着 从squid到用户的响应TCP包欺骗了源地址。lan0过滤规则导致路由器拒绝这些包。为了让cache拦截生效,网络管理员须移除lan0规则。这样 就让网络有漏洞,从而易遭拒绝服务攻击。
我在先前的章节里描述过,客户端在打开连接之前必须先进行DNS查询。在某些防火墙环境中,这样做可能有问题。你想进行HTTP拦截的主机必须能 够查询DNS。如果客户端了解自己正使用代理(因为手工配置或代理自动配置),它通常就不去解析主机名。代替的,它简单的将完整URL转发给squid, 由squid来查询原始服务器的IP地址。
另一个小问题是,squid接受任意目的IP地址的连接。例如,某个web站点当机了,但它仍然有DNS记录存在。squid伪装这个站点接受TCP连接。客户端会认为该站点仍然在运行,因为连接有效。当squid连接到原始服务器失败时,它强迫返回错误消息。
万一形势不清,HTTP拦截在初次使用时有些棘手或困难。许多不同的组件必须组合工作,并且要配置正确。甚至,从内存中恢复整个配置也很困难。我强烈建议你在将其应用于生产环境之前,先建立测试环境。一旦你让它正常运行,请记录每一步细节。
9.3 网络设备
现在你了解了cache拦截的相关细节,让我们看看如何实际让它工作。我们先配置网络设备,它们用来拦截HTTP连接。
9.3.1 内置Squid
在该配置中,你无需交换或网络路由设备来拦截HTTP连接。代替的,squid运行的Unix系统,也就是路由器(或网桥),请见图9-2。
该配置本质上跳过了9.1章的头三步。squid主机充当网络路由器,它接受HTTP连接包。假如你采用此方法,请直接跳到9.4章。
9.3.2 四层交换
许多单位使用四层交换机来支持HTTP拦截。这些产品提供更多的功能,例如健壮性检测和负载均衡。我在这里仅仅讲讲拦截。关于健壮性检测和负载均 衡的信息,请见O'Reilly's Server Load Balancing and Load Balancing Servers, Firewalls, and Caches (John Wiley & Sons). 下面的章节包含了许多产品和技术的示例配置。
9.3.2.1 Alteon/Nortel
下面的配置来自ACEswitch 180和Alteon's WebOS 8.0.21。网络设置请见图9-4。
客户端连接到端口1,通过端口2连接到因特网,squid运行在端口3。下面的行是交换机的/cfg/dump命令的输出。你无须敲入所有这些 行。甚至,在Alteon的新版软件里,某些命令可能改变了。注意Alteon把这个功能叫做Web Cache重定向(WCR)。如下是处理步骤:
1.首先,你必须分配给Alteon交换机一个IP地址。这是必要的,以便交换机能检查squid的存活状态。
/cfg/ip/if 1 ena addr 172.16.102.1 mask 255.255.255.0 broad 172.16.102.255
2.Alteon的WCR属于服务负载均衡(SLB)配置。所以,必须使用如下命令在交换机上激活SLB功能:
/cfg/slb on
3. 现在,用squid的IP地址定义real server:
/cfg/slb/real 1 ena rip 172.16.102.66
4. 必须定义一个组,并分配给real server一个组号:
/cfg/slb/group 1 health tcp add 1
5. 下一步定义2个过滤规则。第1条规则匹配HTTP连接(目的端口是80的TCP包),并重定向它们到组1里的server。第2条规则匹配所有其他数据包,并正常转发它们。
/cfg/slb/filt 1 ena action redir sip any smask 0.0.0.0 dip any dmask 0.0.0.0 proto tcp sport any dport http group 1 rport 0
/cfg/slb/filt 224 ena action allow sip any smask 0.0.0.0 dip any dmask 0.0.0.0 proto any
6. 最后一步是给SLB配置指定的交换端口。在端口1上,处理客户端连接(这也是客户端连接的端口),并增加2条过滤规则。在端口2上,仅须配置它正常服务(例如,向上连接到Internet):
cfg/slb/port 1 client ena filt ena add 1 add 224
/cfg/slb/port 2 server ena
为了验证HTTP拦截配置正确并工作良好,你可以使用/stats/slb和/info/slb菜单里的命令。/info/slb/dump是快速有效的查看整个SLB配置的方法:
>> Main# /info/slb/dump
Real server state: 1: 172.16.102.66, 00:c0:4f:23:d7:05, vlan 1, port 3, health 3, up
Virtual server state:
Redirect filter state: 1: dport http, rport 0, group 1, health tcp, backup none real servers: 1: 172.16.102.66, backup none, up
Port state: 1: 0.0.0.0, client filt enabled, filters: 1 224 2: 0.0.0.0, server filt disabled, filters: empty 3: 0.0.0.0 filt disabled, filters: empty
在该输出里,注意到交换机显示Squid在端口3上可到达,并且运行正常。你也能见到过滤规则1应用到端口1。在端口状态节里,端口1定义为客户端连接端口,端口2简单的标记为服务端口。
/stats/slb/real命令显示real server(squid)的有用统计:
>> Main# /stats/slb/real 1 ------------------------------------------------------------------
Real server 1 stats: Health check failures: 0 Current sessions: 41 Total sessions: 760 Highest sessions: 55 Octets: 0
大部分统计与任务(例如TCP连接)数量相关。假如再次运行该命令,总共的任务计数会增加。
最后,/stats/slb/group命令显示几乎同样的信息:
>> Main# /stats/slb/group 1 ------------------------------------------------------------------
Real server group 1 stats:
Current Total Highest Real IP address Sessions Sessions Sessions Octets ---- --------------- -------- ---------- -------- --------------- 1 172.16.102.66 65 2004 90 0 ---- --------------- -------- ---------- -------- --------------- 65 2004 90 0
假如不止1个real server在组里,该输出会更有趣。
9.3.2.2 Foundry
下面的配置示例来自ServerIron XL,运行的软件版本是07.0.07T12。跟前面一样,客户端在端口1,Internet连接在端口2,squid运行在端口3。然而,这样的配置少 了点东西,因为这里可以激活HTTP全局拦截。Foundry的cache拦截的名字叫做Transparent Cache Switching(TCS)。请参考图9-4。
首先请给交换机分配1个IP地址,以便执行健壮性检测: ip address 172.16.102.1 255.255.255.0
Foundry允许你在特定端口上激活或禁用TCS。然而简单起见,这里全局激活它: ip policy 1 cache tcp http global
在该行里,cache是针对TCS功能的关键字。下1行定义web cache,我定义其名字为squid1,并且告诉交换机它的IP地址: server cache-name squid1 172.16.102.66
最后的步骤是将web cache加进cache组里: server cache-group 1 cache-name squid1
假如在转发连接时有问题,请参阅show cache-group命令的输出:
ServerIron#show cache-group
Cache-group 1 has 1 members Admin-status = Enabled Active = 0
Hash_info: Dest_mask = 255.255.255.0 Src_mask = 0.0.0.0
Cache Server Name Admin-status Hash-distribution squid1 6 3
HTTP Traffic From <-> to Web-Caches
Name: squid1 IP: 172.16.102.66 State: 6 Groups = 1
Host->Web-cache Web-cache->Host State CurConn TotConn Packets Octets Packets Octets Client active 441 12390 188871 15976623 156962 154750098 Web-Server active 193 11664 150722 151828731 175796 15853612 Total 634 24054 339593 167805354 332758 170603710
某些输出有些模糊,但通过重复该命令,并且观察计数器的增长,你能了解拦截是否在进行。
show server real提供几乎同样的信息:
ServerIron#show server real squid1
Real Servers Info
Name : squid1 Mac-addr: 00c0.4f23.d705
IP:172.16.102.66 Range:1 State:Active Wt:1 Max-conn:1000000
Src-nat (cfg:op):(off:off) Dest-nat (cfg:op):(off:off)
squid1 is a TRANSPARENT CACHE in groups 1
Remote server : No Dynamic : No Server-resets:0
Mem:server: 02009eae Mem:mac: 045a3714
Port State Ms CurConn TotConn Rx-pkts Tx-pkts Rx-octet Tx-octet Reas
---- ----- -- ------- ------- ------- ------- -------- -------- ---- http active 0 855 29557 379793 471713 373508204 39425322 0
default active 0 627 28335 425106 366016 38408994 368496301 0
Server Total 1482 57892 804899 837729 411917198 407921623 0
最后,使用show logging命令来观察交换机是否显示squid正常或异常:
ServerIron#show logging ...
00d00h11m51s:N:L4 server 172.16.102.66 squid1 port 80 is up
00d00h11m49s:N:L4 server 172.16.102.66 squid1 port 80 is down
00d00h10m21s:N:L4 server 172.16.102.66 squid1 port 80 is up
00d00h10m21s:N:L4 server 172.16.102.66 squid1 is up
注意ServerIron认为服务运行在80端口。以后你会见到squid运行在3128端口的示例。包过滤规则实际上将包的目的地址从80改变为3128。这导致一些与状态检测有关的有趣结果,我在9.3.2.5节里会讲到。
9.3.2.3 Extreme Networks
在该示例里,硬件是Summit1i,软件版本是6.1.3b11。再次将客户端分配在端口1,Internet在端口2,squid在端口3。网络配置见图9-5。
Extreme交换机仅仅对在不同子网间进行路由的数据包进行HTTP连接的拦截。换句话说,如果你配置Extreme交换机使用二层模式(单一 VLAN里),就不能将包转发给squid。为了让HTTP拦截正常工作,必须给用户,Squid,和Internet配置不同的VLAN。
configure Default delete port 1-8
create vlan Users configure Users ip 172.16.102.1 255.255.255.192 configure Users add port 1
create vlan Internet configure Internet ip 172.16.102.129 255.255.255.192 configure Internet add port 2
create vlan Squid configure Squid ip 172.16.102.65 255.255.255.192 configure Squid add port 3
下一步是激活和配置交换机的路由:
enable ipforwarding configure iproute add default 172.16.102.130
最后,配置交换机重定向HTTP连接到Squid:
create flow-redirect http tcp destination any ip-port 80 source any configure http add next-hop 172.16.102.66 9.3.2.4 Cisco Arrowpoint
下类配置基于我以前的测试笔记。然而,最近我没有使用这类型的交换机,不能确保如下命令仍然正确:
circuit VLAN1 ip address 172.16.102.1 255.255.255.0
service pxy1 type transparent-cache ip address 172.16.102.66 port 80 protocol tcp active
owner foo content bar add service pxy1 protocol tcp port 80 active
9.3.2.5 关于HTTP服务和健壮性检测的评论
在上面的示例里,路由器/交换机都直接转发包,不会改变目的TCP端口。在9.4章里用到的包过滤规则改变了目的端口。如果试图在同一主机上运行HTTP服务和squid,那么就产生了一个有趣的问题。
为了在3128端口运行Squid的同时,还要在80端口运行HTTP,包过滤配置必须有1条特殊的规则,它接受到本机HTTP服务的TCP连 接。否则,连接会直接转交给Squid。该规则易于建立。假如目的端口是80,并且目的地址是服务器的,那么主机正常接受这个包。然而所有的拦截包有外部 目的地址,所以它们不会匹配该规则。
然而,当路由器/交换机进行HTTP健壮性检测时,它连接到服务器的IP地址。这样,健壮性检测的数据包匹配了上述规则,它不会转交给Squid。路由器/交换机检测了错误的服务。假如HTTP服务down掉了,而squid还在运行,那健壮性检测就产生错误结果。
解决这个问题的一些选择是: 1.不要在Squid主机上运行HTTP服务; 2.增加1条特殊的包过滤规则,将来自路由器/交换机的状态检测的包转交给squid; 3.配置路由器/交换机,改变目的端口为3128; 4.禁止4层状态检测。
9.3.3 Cisco策略路由
策略路由与4层交换并非不同。它在Cisco和其他公司的路由产品上执行。主要的区别是策略路由不包括任何健壮性检测。这样,假如squid超载 或完全不可响应,路由器还会继续转发包到squid,而不是将包路由到原始服务器。策略路由要求squid位于路由器直接相连的子网中。
在本示例里,使用了Cisco 7204路由器,运行IOS Version 12.0(5)T。网络配置与前面的一样,见图9-5。
首先的配置步骤是定义访问列表,匹配来自客户端的到80端口的数据包。必须确保Squid发起的到80端口的数据包不会被再次拦截。做到这点的方法之一是,定义1个特殊规则,拒绝来自squid的数据包,紧跟1条规则允许所有其他的数据包:
access-list 110 deny tcp host 172.16.102.66 any eq www access-list 110 permit tcp any any eq www
另外,如果Squid和用户位于不同的子网,你可以仅仅允许来自用户所在网络的数据包: access-list 110 permit tcp 10.102.0.0 0.0.255.255 any eq www
下一步是定义路由映射。在这里你告诉路由器转发拦截包到何处去:
route-map proxy-redirect permit 10 match ip address 110 set ip next-hop 172.16.102.66
这些命令表明,“假如IP地址匹配访问列表110,就转发该包到172.16.102.66”。在route-map行的数字10是一个序列号,假如你有多个路由映射的话。最后一步是应用路由映射到客户端连接的接口:
interface Ethernet0/0 ip policy route-map proxy-redirect
IOS不对策略路由提供很多调试方法。然而,show route-map命令足够可用:
router#show route-map proxy-redirect route-map proxy-redirect, permit, sequence 10
Match clauses: ip address (access-lists): 110 Set clauses: ip next-hop 172.16.102.66 Policy routing matches: 730 packets, 64649 bytes
9.3.4 Web Cache Coordination协议
Cisco对4层交换技术的响应叫做Web Cache Coordination Protocol(WCCP).WCCP在许多方面与典型的4层拦截不同。
首先,拦截包被封装在GRE(路由封装类)里。这点允许数据包跨子网传输,也就意味着squid不必直接连在路由器上。因为数据包是封装的,squid主机必须对其进行解包。并非所有的Unix系统有解开GRE包的代码。
第二个不同是,路由器如何决定将负载分摊到多个cache上。事实上,路由器不做这个决定,由cache来做。当路由器有一组支持WCCP的 cache时,其中一个cache主动成为组的领导。由领导cache来决定如何分摊负载和通知路由器。在路由器重定向然任何连接之前,这是一个额外的必 要步骤。
因为WCCP使用GRE,路由器可能强迫要求将来自HTTP请求的大TCP包分割成片断。幸运的是,这点不会经常发生,因为大部分HTTP请求比 以太网MTU size(1500字节)要小。默认的TCP和IP包头部是20字节,这意味着以太网帧能实际携带1460字节的数据。GRE封装在GRE头部增加了20 字节,另外在第二个IP头部增加了20字节。这样来自客户端的正常的1500字节的TCP/IP包,在封装后变成了1540字节。这样数据包就太大而不能 在单个以太网帧里传输,所以路由器将原始包分割成两个片断。
9.3.4.1 WCCPv1
该节的配置示例在运行IOS Version 12.0(5)T的Cisco 7204路由器上测试。网络配置跟图9-5同。
首先,在IOS配置中输入如下两行,激活路由器的WCCP:
ip wccp version 1 ip wccp web-cache
接着,必须在单独的路由器接口上激活WCCP。在HTTP包离开路由器的接口上激活WCCP,也就是路由器连接到外部原始服务器或Internet网关的接口:
interface Ethernet0/1 ip address 172.16.102.129 255.255.255.192 ip wccp web-cache redirect out
请确认保存了配置改变。
你也许想使用访问列表来阻止某些web站点的拦截。可以使用访问列表来防止循环转发。例如:
! don't re-intercept connections coming from Squid: access-list 112 deny tcp host 172.16.102.66 any eq www
! don't intercept this broken web site access-list 112 deny tcp any 192.16.8.7 255.255.255.255 eq www
! allow other HTTP traffic access-list 110 permit tcp any any eq www
ip wccp web-cache redirect-list 112
路由器不发送任何数据到squid,直到squid宣称它自己是路由器。我在9.5.1节里解释如何配置squid的WCCP。
9.3.4.2 WCCPv2
当前标准的Squid发布仅支持WCCPv1。然而,可在squid-cache.org/" target="_blank">http://devel.squid-cache.org/找到WCCPv2的补丁。该代码仍是实验性的。
注意从路由器发送到Squid的GRE包,包含了额外的4个字节。WCCPv2在GRE头部和封装的IP包之间,插入了一个重定向头部。也许需要修改内核代码来计算这个额外的头部。
9.3.4.3 调试
IOS提供许多命令来监视和调试WCCP。show ip wccp web-cache命令提供一些基本的信息:
router#show ip wccp web-cache
Global WCCP information: Router information: Router Identifier: 172.16.102.129 Protocol Version: 1.0 Service Identifier: web-cache Number of Cache Engines: 1 Number of routers: 1 Total Packets Redirected: 1424 Redirect access-list: -none- Total Packets Denied Redirect: 0 Total Packets Unassigned: 0 Group access-list: -none- Total Messages Denied to Group: 0 Total Authentication failures: 0
欲了解更多细节,在前叙命令后加一个detail单词:
router#show ip wccp web-cache detail
WCCP Cache-Engine information:
IP Address: 172.16.102.66 Protocol Version: 0.4 State: Usable Initial Hash Info: 00000000000000000000000000000000 00000000000000000000000000000000 Assigned Hash Info: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF Hash Allotment: 256 (100.00%) Packets Redirected: 1424 Connect Time: 00:17:40
这里可以看到squid的IP地址和状态。假如不止一个cache与路由器会话,那么hash分配信息看起来不同。大多数情况下,每个cache接受到相等的hash值。
注意第二条命令输出的协议版本值,与第一条命令的不一样。不幸的是,赋予了版本号太多的意义。show ip wccp web-cache命令看起来报告WCCP协议的主版本号(例如1或2),然而show ip wccp web-cache detail的版本号看起来匹配Squid的wccp_version指令的值。
9.4 操作系统配置
为了让cache拦截正常工作,必须在操作系统中激活某些网络功能。首先,必须激活IP包转发。这就允许操作系统接受目的地址是外部IP的数据包。接着,必须激活和配置内核中的相关代码,以重定向外部包到Squid。
9.4.1 Linux
本节的指导适合2.4系列Linux内核。我使用RedHat Linux7.2(内核是2.4.7-10)。假如你使用的版本不同,那可能不能运行。建议搜索下Squid的FAQ或其他地方,找到关于内核的更新的或历史的信息。
在我测试iptables过程中,不必激活IP转发。然而,你也可以试试在一开始就激活它,并在一切运行良好后,看看能否禁掉它。激活包转发的最好的方法是在/etc/sysctl.conf文件里增加如下行:
net.ipv4.ip_forward = 1
一般来说,在HTTP拦截生效前,不必编译新内核。假如你不知道如何配置和创建新Linux内核,请参阅O'Reilly's Running Linux by Matt Welsh, Matthias Kalle Dalheimer, and Lar Kaufman。当你配置内核时,请确认如下选项被激活:
o General setup Networking support (CONFIG_NET=y) Sysctl support (CONFIG_SYSCTL=y)
o Networking options Network packet filtering (CONFIG_NETFILTER=y) TCP/IP networking (CONFIG_INET=y) Netfilter Configuration Connection tracking (CONFIG_IP_NF_CONNTRACK=y) IP tables support (CONFIG_IP_NF_IPTABLES=y) Full NAT (CONFIG_IP_NF_NAT=y) REDIRECT target support (CONFIG_IP_NF_TARGET_REDIRECT=y)
o File systems /proc filesystem support (CONFIG_PROC_FS=y)
另外,请确认该选项没被激活:
o Networking options
Fast switching (CONFIG_NET_FASTROUTE=n)
重定向外部数据包到squid的代码是Netfilter软件的一部分。如下是发送HTTP拦截连接到squid的规则:
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 3128
Linux内核维护许多不同的tables。-t nat选项表明我们正在修改NAT(网络地址转换)表。本质上,我们使用iptables将原始服务器的TCP/IP地址转换为squid的本地TCP/IP地址。
每个iptables表有许多链。-A PREROUTING表明我们增加了一条规则到内建的链叫做PREROUTING。PREROUTING链仅对从外部网络进入系统的数据包有效。
接下来的三个选项决定哪个包匹配该规则。-i eth0选项限制规则仅对eth0接口上接受的数据包有效。-p tcp选项指定TCP包,--dport 80指定包的目的端口是80。假如这三个条件都是true,那么包匹配该规则。
-j REDIRECT选项表明对匹配规则的包,采取何种动作。REDIRECT是内建的动作名,它导致iptables改变包的目的地址为127.0.0.1。--to-port 3128选项也指示iptables改变目的TCP端口为3128。
假如你在squid主机上运行HTTP服务(例如Apache),就必须增加另外的iptables规则。该必要规则允许连接到HTTP服务。否 则,REDIRECT规则导致iptables转发连接到squid的3128端口。可以使用-I选项在列表顶部插入一条新规则:
iptables -t nat -I PREROUTING -i eth0 -p tcp -d 172.16.102.66 --dport 80 -j ACCEPT
一旦确认所有iptables规则工作正确,记得运行如下命令来保存配置:
/sbin/service iptables save
将当前规则保存到/etc/sysconfig/iptables,当系统重启时,这些规则自动载入。
9.4.1.1 Linux和WCCP
2.4版本的Linux内核自带1个GRE伪装接口。然而,它不能解码WCCP任务里封装的GRE包。问题看起来在于路由器设置了WCCP/GRE包的协议类型域为0x883E。Linux的GRE驱动不知道如何处理这类型包,因为它不了解0x883E类型的协议。
可以试试给Linux打GRE模块的补丁,以便它能在WCCP下工作。Squid FAQ包含了对这个补丁的链接。然而,假如使用WCCP指定的Linux模块,事情会容易些。可以在这里找到它:squid-" target="_blank"> cache.org/WCCP-support/Linux/ip_wccp.c
必须编译ip_wccp.c文件为可装载内核模块。有点棘手的是,依赖于内核版本的不同,编译选项可能不同。你可以进入内核源代码目录,敲入make modules并观察编译器命令的滚动。然后拷贝这些命令中的一个,然后改变最后一个参数为ip_wccp.c。如下是我在2.4.7-10 Linux内核中使用的命令:
% gcc -Wall -D_ _KERNEL_ _ -I/usr/src/linux-2.4.7-10/include \ -DMODULE -DMODVERSIONS -DEXPORT_SYMBAB \ -include /usr/src/linux-2.4.7-10/include/linux/modversions.h \ -O2 -c ip_wccp.c
gcc命令在当前目录会生成ip_wccp.o文件。下一步使用insmod命令,装载模块到内核中:
# insmod ip_wccp.o
注意ip_wccp模块接受来自任何源地址的GRE/WCCP包。换句话说,恶意用户可能发送数据到squid cache。假如使用该模块,应该安装一条iptables规则,拒绝外部的GRE包。例如:
# iptables -A INPUT -p gre -s 172.16.102.65 -j ACCEPT # iptables -A INPUT -p gre -j Drop
不要忘记敲入/sbin/service iptables save命令来保存配置。
9.4.2 FreeBSD
本节的例子基于FreeBSD-4.8,并可以在任何FreeBSD-4和5系列的后续版本上运行。
要激活IP包转发,在/etc/sysctl.conf中增加如下行:
net.inet.ip.forwarding=1
需要在内核中激活2个特殊选项。假如你不知道如何编译内核,参见FreeBSD Handbook第9章(). 编辑内核配置文件,确保有如下行:
options IPFIREWALL options IPFIREWALL_FORWARD
假如squid主机位于无人照看的机房中,我也推荐使用IPFIREWALL_DEFAULT_TO_ACCEPT选项。假如你被防火墙的规则困扰,仍然可以登陆系统中。
ipfw命令告诉内核重定向拦截连接到squid:
/sbin/ipfw add allow tcp from 172.16.102.66 to any out /sbin/ipfw add allow tcp from any 80 to any out /sbin/ipfw add fwd 127.0.0.1,3128 tcp from any to any 80 in /sbin/ipfw add allow tcp from any 80 to 172.16.102.66 in
第一条规则匹配squid主机发起的数据包。它确保外出的TCP连接不会被重新定向回squid。第二条规则匹配squid响应客户端的数据包。 我在这里列出它,因为随后会有另外的ipfw规则,这些规则会拒绝这些包。第三条规则实际重定向进来的连接到squid。第四条规则匹配从原始服务器返回 squid的数据包。这里又一次假设随后会有相应的拒绝规则。
假如在squid主机上运行HTTP服务,就必须增加另外的规则,它放过,而不是重定向,目的地址是原始服务器的TCP包。下列规则在fwd规则之前:
/sbin/ipfw add allow tcp from any to 172.16.102.66 80 in
FreeBSD典型的将ipfw规则存储在/etc/rc.firewall里。一旦你确认规则设置正确,记得保存它们。将如下行加入/etc/rc.local文件,让FreeBSD在启动时自动运行/etc/rc.firewall脚本。
firewall_enable="YES"
9.4.2.1 FreeBSD和WCCP
FreeBSD版本4.8和后续版本内建了对GRE和WCCP的支持。早期的版本需要补丁,你可以在这里找到: squid-cache.org/" target="_blank">cache.org/WCCP-support/FreeBSD/ . 内建代码的性能非常好,它是真正的内核组织编写的。可能也需要编译支持GRE的新内核。将如下行加入内核配置文件里:
pseudo-device gre
对Freebsd-5,使用device代替了pseudo-device。当然,你也需要前面章节里提到的FIREWALL选项。
在安装和重启了新内核后,必须配置GRE通道来接受来自路由器的GRE包。例如:
# ifconfig gre0 create # ifconfig gre0 172.16.102.66 172.16.102.65 netmask 255.255.255.255 up # ifconfig gre0 tunnel 172.16.102.66 172.16.102.65 # route delete 172.16.102.65
ifconfig命令在gre0接口上,增加了一个到路由器(172.16.102.65)的路由表入口。我发现必须删除该路由,以便squid能与其他路由器会话。
你也许想或必须对来自路由器的GRE包,增加一条ipfw规则:
/sbin/ipfw add allow gre from 172.16.102.65 to 172.16.102.66
9.4.3 OpenBSD
本节的示例基于OpenBSD 3.3
为了激活包转发,在/etc/sysctl.conf文件里增加该行:
net.inet.ip.forwarding=1
现在,在/etc/pf.conf文件里增加如下类似行,配置包过滤规则:
rdr inet proto tcp from any to any port = www -> 127.0.0.1 port 3128 pass out proto tcp from 172.16.102.66 to any pass out proto tcp from any port = 80 to any pass in proto tcp from any port = 80 to 172.16.102.66
假如你没有使用OpenBSD的包过滤器,需要在/etc/rc.conf.local文件里增加一行来激活它:
pf=YES 9.4.3.1 OpenBSD和WCCP 首先,增加如下行到/etc/sysctl.conf文件,告诉系统接受和处理GRE和WCCP包:
net.inet.gre.allow=1 net.inet.gre.wccp=1
然后,用如下命令配置GRE接口:
# ifconfig gre0 172.16.102.66 172.16.102.65 netmask 255.255.255.255 up # ifconfig gre0 tunnel 172.16.102.66 172.16.102.65 # route delete 172.16.102.65
跟Freebsd一样,我发现必须删除ifconfig自动产生的路由。最后,依赖于包过滤器的配置,必须增加一条规则以允许GRE包:
pass in proto gre from 172.16.102.65 to 172.16.102.66
9.4.4 在NetBSD和其他系统上的IPFilter
本节的示例基于NetBSD 1.6.1。它们也能运行在Solaris,HP-UX,IRIX,和Tru64上,既然这些系统本身就配备了IPFilter.
激活NetBSD的包转发,将如下行加进/etc/sysctl.conf: net.inet.ip.forwarding=1
然后,将如下行插入NAT配置文件/etc/ipnat.conf中: rdr fxp0 0/0 port 80 -> 172.16.102.66 port 3128 tcp
你的接口名可能与本例的fxp0不同。
9.4.4.1 NetBSD和WCCP
我不能在NetBSD上运行WCCP,即使打了GRE补丁来接受WCCP包。该问题看起来根源在IPFilter rdr规则阻塞了特定的端口。来自路由器的包通过NetBSD的gre0接口(在这里它们被解包)。然而,包回到路由器时,走另一条通道,未被封装并且不 走同一网络接口。这样,IPFilter代码没有将squid的本地IP地址转换回原始服务器的地址。 9.5 配置Squid 假如你使用Linux 2.4和iptables,在运行./configure时,可使用--enable-linux-netfilter选项。它激活某些Linux的特殊 代码,以发现发起请求的原始服务器的IP地址。Squid正常情况下从Host头部,得到原始服务器的名字(和/或地址)。--enable-linux -netfilter功能仅对没有Host头部的请求来说是必要的。统计显示几乎所有的请求有Host头部,所以实际中可以不使用--enable- linux-netfilter选项。
假如正在使用IPFilter包(NetBSD,Solaris,或其他),因为同样的理由,你应该使用--enable-ipf- transparent选项。在OpenBSD上,请使用--enable-pf-transparent选项。每次运行./configure时,必须 重编译squid,见3.8章的描述。
在运行了./configure和重编译了squid后,可以编辑squid.conf文件。作为起点,请确认下列指令定义了给定的值:
httpd_accel_host virtual httpd_accel_port 80 httpd_accel_uses_host_header on httpd_accel_with_proxy on httpd_accel_single_host off
http_accel_host指令是关键。它指示squid接受包含局部URI的HTTP请求。 httpd_accel_uses_host_header被激活,允许squid使用Host头部来重新构建完整URI。virtual关键字指示 squid在缺乏Host头部时,将原始服务器的IP地址放进URL里。
httpd_accel_with_proxy指令控制squid是否既接受HTTP服务(部分URI)请求,又接受代理(完整URI)请求。在 cache拦截里,它应该被激活。如果没有用户明确的配置使用squid做代理,那即使httpd_accel_with_proxy没被激活, squid也能工作。
httpd_acces_single_host指令正常情况下被禁止,在早期版本的squid里,它可能被默认激活。在cache拦截里,它应明确被禁止。
假如拦截不止针对80端口,你也许该将httpd_accel_port设为0。见附录A的更多信息。
假如你没有使用WCCP,就该准备开始发起拦截会话到squid了。通过使用浏览器来上网冲浪,或者使用squidclient发起测试请求,就可以放手一试。假如你使用WCCP,那么还有许多步骤要完成。
9.5.1 配置WCCPv1
路由器不发送任何会话到squid,直到squid宣称它自己是路由器。为了让squid那样做,在squid.conf中增加如下行:
wccp_router 172.16.102.65 wccp_version 4
路由器有多个接口。请确认使用与squid相连的接口的IP地址。这点是必要的,因为来自路由器的WCCP消息,将源IP地址设置为外出接口的地址。假如源地址不匹配wccp_router值,squid会拒绝WCCP消息。
WCCPv1文档规定4作为协议版本号。然而,某些用户报告,Cisco IOS 11.2仅支持协议版本3。假如你使用该版本的IOS,请在squid.conf里改变版本号:
wccp_version 3
9.6 调试问题
HTTP拦截比较复杂,因为许多不同设备必须组合正确工作。为了帮助你跟踪问题,如下是一个问题解答检查列表:
? 客户端数据包正在通过路由器/交换机吗? 在简单网络里,这点显而易见。你可以trace线缆并观察指示灯的活动闪烁。然而在大而复杂的网络,数据包可能走不同的路线。假如你的组织够大, 并有网络sniffer设备,就可以观察线路中web客户端的请求数据包。低技术的方法是,断开有问题的线路,并观察是否影响客户端的web浏览。
? 路由器/交换机配置是否正确? 你也许要再次检查路由器/交换机配置。假如你已配置了某个接口,那能否确保它正确呢? 是否新的配置真正在设备上运行?也许在你保存配置之前,路由器/交换机已重启了。在改变生效前,你或许需要reboot设备。
? 交换机/路由器能与squid主机会话吗? 能从路由器/交换机上ping通squid吗?大部分4层拦截配置要求网络设备和squid在同一子网里。登陆路由器/交换机,确认能ping通squid的IP地址。
? 交换机/路由器相信squid在运行吗? 许多传输拦截设备不会发送会话到squid,除非它们知道squid是健壮的。使用调试命令来预览squid的健壮性状态。也许会发现三层健壮性检测(例如ICMP ping)比四层检测(例如HTTP)更容易,它使网络设备更容易将squid标记为存活状态。
? Squid实际在运行吗? 请再次确认squid真正在运行,特别是在系统近期重启过的情况下。
? 数据包正在抵达squid主机吗? 使用tcpdump能见到拦截的TCP连接。如下是示例: # tcpdump -n -i eth0 port 80
假如使用WCCP,请检查来自路由器的GRE包: # tcpdump -n -i eth0 ip proto gre
假如没有看到tcpdump的任何输出,则路由器/交换机可能没有发送任何数据。在这种情况下,返回到以前的建议。
注意,假如设备正使用四层健壮性检测,你可以在tcpdump的输出里见到这些。健壮性检测来自路由器/交换机的IP地址,所以它们容易被认出。 假如你见到健壮性检测,但没有其他数据,那可能意味着路由器/交换机正把squid的响应理解为不健壮。例如,设备可能想见到200(OK)响应,但 squid返回一个错误,例如401(未授权)或404(未发现)。请对access.log运行tail -f命令。
? 激活了IP转发吗? 请再次确认squid运行的操作系统配置了IP包转发。假如没有,主机可能会丢弃拦截数据包,因为目的IP地址并非本地。
? 配置包过滤了吗? 请确认包过滤器(例如ipfw,iptables,pf等)配置正确。当每件事都运行良好时,你能定期运行命令来显示过滤规则,并观察计数器增长。例如:
# ipfw show 300 ; sleep 3; ipfw show 300 00300 86216 8480458 fwd 127.0.0.1,3128 tcp from any to any 80 in 00300 86241 8482240 fwd 127.0.0.1,3128 tcp from any to any 80 in
注意该示例在FreeBSD上,包和字节计数器(第二和第三列)正在增长。
? 环路接口起来和配置了吗? 假如有一条规则转发/重定向包到127.0.0.1,请确认环路接口(例如lo0,lo等)起来了,并配置过它。假如没有,内核简单的跳过这条转发/重定向规则。
? WCCP/GRE包被正确解开了吗? 假如使用WCCP,请确认GRE包被正确解开。假如因为某些理由,系统不知道该如何处理GRE包,那就在netstat -s的输出里会见到"unknown/unsupported protocol"计数器在增长。
# netstat -s | grep unknown
46 packets for unknown/unsupported protocol
假如OS有GRE接口,请频繁运行netstat -i命令,观察不断增长的包数量:
# netstat -in | grep ^gre0
Name Mtu Network Address Ipkts Ierrs Opkts Oerrs Coll gre0 1476 304452 0 0 4 0
另外,在GRE接口上运行tcpdump: # tcpdump -n -i gre0
? Squid能响应客户端吗? 有可能路由器/交换机能发送包到squid,但squid不能将包发送回客户端。这种情况可能发生在:防火墙过滤规则拒绝外出数据包,或squid没有到客户端IP地址的路由。为了检查这种情况,请运行netstat -n并观察SYN_RCVD状态的sockets:
% netstat -n
Active Internet connections
Proto Recv-Q Send-Q Local Address Foreign Address (state)
tcp4 0 0 10.102.129.246.80 10.102.0.1.36260 SYN_RCVD tcp4 0 0 10.102.129.226.80 10.102.0.1.36259 SYN_RCVD tcp4 0 0 10.102.128.147.80 10.102.0.1.36258 SYN_RCVD tcp4 0 0 10.102.129.26.80 10.102.0.2.36257 SYN_RCVD tcp4 0 0 10.102.129.29.80 10.102.0.2.36255 SYN_RCVD tcp4 0 0 10.102.129.226.80 10.102.0.1.36254 SYN_RCVD tcp4 0 0 10.102.128.117.80 10.102.0.1.36253 SYN_RCVD tcp4 0 0 10.102.128.149.80 10.102.0.1.36252 SYN_RCVD
假如你看到这些,请使用ping和traceroute来确认squid能与客户端双向通信。
? Squid能与原始服务器会话吗? 假如squid不能连接到原始服务器,拦截HTTP连接会无法进行。如果这点发生,netstat会显示许多连接在SYN_SENT状态:
% netstat -n
Active Internet connections
Proto Recv-Q Send-Q Local Address Foreign Address (state)
tcp4 0 0 172.16.102.66.5217 10.102.129.145.80 SYN_SENT tcp4 0 0 172.16.102.66.5216 10.102.129.224.80 SYN_SENT tcp4 0 0 172.16.102.66.5215 10.102.128.71.80 SYN_SENT tcp4 0 0 172.16.102.66.5214 10.102.129.209.80 SYN_SENT tcp4 0 0 172.16.102.66.5213 10.102.129.62.80 SYN_SENT tcp4 0 0 172.16.102.66.5212 10.102.129.160.80 SYN_SENT tcp4 0 0 172.16.102.66.5211 10.102.128.129.80 SYN_SENT tcp4 0 0 172.16.102.66.5210 10.102.129.44.80 SYN_SENT tcp4 0 0 172.16.102.66.5209 10.102.128.73.80 SYN_SENT tcp4 0 0 172.16.102.66.5208 10.102.128.43.80 SYN_SENT
再次用ping和traceroute来确认squid能与原始服务器会话。
? 外出连接正被拦截吗? 假如squid能ping通原始服务器,并且仍然见到大量的连接在SYN_SENT状态,那么路由器/交换机可能正在拦截squid的外出TCP 连接。在某些情况下,squid能检测到这种转发循环,并写警告日志到cache.log。如此的转发死循环能迅速耗光squid的所有文件描述符,这样 也会在cache.log里产生警告。
假如你怀疑这个问题,请使用squidclient程序来发起一些简单的HTTP请求。例如,该命令发起一条直接到原始服务器的HTTP请求: % /usr/local/squid/bin/squidclient -p 80 -h slashdot.org /
假如该命令成功,你可以在屏幕上见到来自Slashdot站点的许多难看的HTML。然后通过squid来试试同样的请求: % /usr/local/squid/bin/squidclient -r -p 3128 -h 127.0.0.1
假如没有在cache.log里看到错误消息,就可以再次在屏幕上看到一些HTML。假如看到转发循环错误,就必须重新配置交换机/路由器,以便它允许squid的外出连接正常通过,而不被拦截。