-
第16章 调试和故障处理
-
16.1 一些通用问题
- 16.1.1 "Failed to make swap directory"
- 16.1.2 "Address already in use"
- 16.1.3 "Could not determine fully qualified hostname"
- 16.1.4 "DNS name lookup tests failed"
- 16.1.5 "Illegal character in hostname"
- 16.1.6 "Running out of filedescriptors"
- 16.1.7 "icmpRecv: Connection refused"
- 16.1.8 在运行一段时间后,Squid变慢了
- 16.1.9 调试访问控制
- 16.2 通过cache.log进行调试
- 16.3 Coredump,断点,和堆栈跟踪
- 16.3.1 不能找到core文件?
- 16.4 重现问题
- 16.5 报告Bug
-
16.1 一些通用问题
- 译后序 ,
-
16.1 一些通用问题
- 16.1.1 "Failed to make swap directory"
- 16.1.2 "Address already in use"
- 16.1.3 "Could not determine fully qualified hostname"
- 16.1.4 "DNS name lookup tests failed"
- 16.1.5 "Illegal character in hostname"
- 16.1.6 "Running out of filedescriptors"
- 16.1.7 "icmpRecv: Connection refused"
- 16.1.8 在运行一段时间后,Squid变慢了
- 16.1.9 调试访问控制
- 16.2 通过cache.log进行调试
- 16.3 Coredump,断点,和堆栈跟踪
- 16.3.1 不能找到core文件?
- 16.4 重现问题
- 16.5 报告Bug ,
- 16.1.1 "Failed to make swap directory"
- 16.1.2 "Address already in use"
- 16.1.3 "Could not determine fully qualified hostname"
- 16.1.4 "DNS name lookup tests failed"
- 16.1.5 "Illegal character in hostname"
- 16.1.6 "Running out of filedescriptors"
- 16.1.7 "icmpRecv: Connection refused"
- 16.1.8 在运行一段时间后,Squid变慢了
- 16.1.9 调试访问控制
第16章 调试和故障处理
16.1 一些通用问题
在讨论通用debug前,我先提起一些经常发生的问题。
16.1.1 "Failed to make swap directory"
Failed to make swap directory /var/spool/cache: (13) Permission denied
这点发生在你运行squid -z,并且squid的用户ID没有对/var/spool目录的写权限的时候。记住假如以root来启动squid,并且没有增加cache_effective_user行,那么squid默认以nobody用户运行。解决方法很简单:
# chown nobody:nobody /var/spool
16.1.2 "Address already in use"
commBind: Cannot bind socket FD 10 to *:3128: Address already in use
这个消息出现在bind()系统调用失败时,因为请求端口已经被其他应用程序所打开。通常,若已有一个squid在运行,而又试图启动第2个squid实例,就会发生这种情况。假如你见到这个错误消息,请使用ps来观察是否squid已经在运行。
Squid使用SO_REUSEADDR socket选项,以便bind()调用总能成功,即使仍有一些残余的socket位于TIME_WAIT状态。若该消息出现,尽管squid没有在运行,但你的操作系统可能在处理这个问题上有bug。重启操作系统是解决问题的一个方法。
另一个可能性是端口(例如3128)当前已被其他应用程序使用。假如你怀疑这点,就可使用lsof程序来发现哪个应用正在该端口上侦听。FreeBSD用户能使用sockstat代替。
16.1.3 "Could not determine fully qualified hostname"
FATAL: Could not determine fully qualified hostname. Please set 'visible_hostname'
假如squid不能识别它自己的完整可验证域名,就会报这个错。如下是squid使用的算法:
- 1) 假如你将squid的HTTP端口绑定在指定的接口地址上,squid试图对该地址执行反向DNS查询。假如成功,查询答案就被用上。
- 2) Squid调用gethostname( )函数,然后使用 gethostbyname( )函数,试着解析其IP地址。假如成功,squid使用后者函数返回的官方主机名串。
假如以上2项技术都不能工作,squid以前面提到的致命错误消息退出。在该情形下,必须使用visible_hostname指令来告诉squid它的主机名。例如:
visible_hostname my.host.name
16.1.4 "DNS name lookup tests failed"
默认情况下,squid在启动前执行一些DNS查询。这点确保你的DNS服务器可到达,并且运行正确。假如测试失败,可在cache.log或syslog里见到如下消息:
FATAL: ipcache_init: DNS name lookup tests failed
假如你在内网里使用squid,squid可能不能查询到它的标准主机名列表。可使用dns_testnames指令来指定你自己的主机名。只要接受到响应,squid就会认为DNS测试成功。
假如你想完全跳过DNS测试,简单的在启动squid时,使用-D命令行选项:
% squid -D ...
16.1.5 "Illegal character in hostname"
urlParse: Illegal character in hostname 'super_bikes.tripod.com'
默认情况下,squid检查URL的主机名部分的字符,假如它发现了非标准的字符,squid会抱怨。参考RFC 1034和1035,名字必须由字母A-Z,数字0-9,以及短横线(-)组成。下划线(_)是最有问题的字符之一。
Squid验证主机名是因为,在某些情形下,DNS对畸形字符的解析会很困难。例如:
% host super_bikes.tripod.com super_bikes.tripod.com has address 209.202.196.70 % ping super_bikes.tripod.com ping: cannot resolve super_bikes.tripod.com: Unknown server error
Squid事先检查主机名,这好过于以后返回Unknown server error消息。然后它会告诉用户主机名包含畸形字符。
某些DNS解析器确实能处理下划线和其他非标准字符。假如你想让squid不检查主机名,请在运行./configure时,使用—disable-hostname-checks选项。假如你允许下划线作为唯一的例外,那么使用—enable-underscores选项。
16.1.6 "Running out of filedescriptors"
WARNING! Your cache is running out of filedescriptors
上述消息出现在squid用完了所有可用文件描述符时。假如这点发生在正常条件下,就有必要增加内核的文件描述符限制,并且重新编译squid。请见3.3.1章。
假如squid成为了拒绝服务攻击的目标,那也会见到这条消息。某些人可能有意或无意的,同时对squid发送成百上千条请求。在这种情形下,可以增加一条包过滤规则,阻止来自恶意地址的TCP进入连接。假如攻击是分布式的,或使用假冒源地址,就很难阻止它们。
转发循环(见10.2章)也可能耗尽squid的所有文件描述符,但仅仅发生在squid不能检测到死循环时。Via头部包含了某个请求遍历过的所有代理的主机名。squid在头部里查找它自己的主机名,假如发现了,就报告这个循环。假如因为某些理由,Via头部从外出或进入HTTP请求里过滤掉了,squid就不能检测到循环。在该情形下,所有文件描述符被循环遍历squid的同一请求迅速耗完。
16.1.7 "icmpRecv: Connection refused"
假如pinger程序没有正确的安装,可见到下列消息:
icmpRecv: recv: (61) Connection refused
不过看起来更象是因为没有打开ICMP socket的权限,pinger立刻退出了。因为该进程未在运行,当squid试图与它会话时,会接受到I/O错误。为了解决该问题,请到源代码目录以root运行:
# make install-pinger
假如成功,你可见到pinger程序有下列文件属主和许可设置:
# ls -l /usr/local/squid/libexec/pinger -rws--x--x 1 root squid 140728 Sep 16 19:58 /usr/local/squid/libexec/pinger
16.1.8 在运行一段时间后,Squid变慢了
看起来更象squid与其他进程,或与它自己,在竞争系统中的内存。当squid进程的内存不再充足时,操作系统被迫从交换空间进行内存读写。这对squid的性能有强烈影响。
为了证实这个想法,请使用top和ps等工具检查squid的进程大小。也检查squid自己的页面错误计数器,见14.2.1.24章的描述。一旦你已确认内存耗费是问题所在,请执行下列步骤来减少squid的内存使用:
- 1. 减少cache_mem值,见附录B。
- 2. 关掉内存池,用该选项:
memory_pools off
- 3. 通过降低一个或多个cache目录的size,减少磁盘cache大小。
16.1.9 调试访问控制
假如访问控制不能正确工作,如下是一些有用帮助。编辑squid.conf文件,设置debug_options行如下:
debug_options ALL,1 33,2
然后,重配置squid:
% squid -k reconfigure
现在,对每个客户端请求以及每个响应,squid都写一条消息到cache.log。该消息包含了请求方式,URI,是否请求/响应被允许或拒绝,以及与之匹配的最后ACL的名字。例如:
2003/09/29 20:22:05| The request GET http://images.slashdot.org:80/topics/topicprivacy.gif is ALLOWED, because it matched 'localhost' 2003/09/29 20:22:05| The reply for GET http://images.slashdot.org/topics/topicprivacy.gif is ALLOWED, because it matched 'all'
知道ACL的名字,并非总能知道相应的http_access行,但也相当接近了。假如必要,可以复制acl行,并给予它们唯一的名字,以便给定的ACL名字仅仅出现在一个http_access规则里。
16.2 通过cache.log进行调试
从13.1章已了解到,cache.log包含了不同的操作消息,squid认为这些消息足够重要,从而告诉了你。我们也将这些作为debug消息考虑。可以使用debug_options指令来控制出现在cache.log里的消息的冗长度。通过增加debug等级,可以见到更详细的消息,有助于理解squid正在做什么。例如:
debug_options ALL,1 11,3 20,3
在squid源代码里的每个debug消息有2个数字特征:1个节和1个等级。节范围从0到100,等级范围从0到10。通常来说,节号对应着源代码的组成成分。换句话说,在单一源文件里的所有消息,有相同的节号。在某些情形下,多个文件使用同一debug节,这意味着某个源文件变得太大,从而被拆分成多个小块。
每个源文件的顶部有一行,用于指示debug节。它看起来如此:
* DEBUG: section 9 File Transfer Protocol (FTP)
我不指望你通过查看源文件来查找节号,所有相关信息定义在表16-1里。
Table 16-1. Debugging section numbers for the debug_options directive
Number | Description | Source file(s) |
0 | Client Database | client_db.c |
1 | Startup and Main Loop | main.c |
2 | Unlink Daemon | unlinkd.c |
3 | Configuration File Parsing | cache_cf.c |
4 | Error Generation | errorpage.c |
5 | Socket Functions | comm.c |
5 | Socket Functions | comm_select.c |
6 | Disk I/O Routines | disk.c |
7 | Multicast | multicast.c |
8 | Swap File Bitmap | filemap.c |
9 | File Transfer Protocol (FTP) | ftp.c |
10 | Gopher | gopher.c |
11 | Hypertext Transfer Protocol (HTTP) | http.c |
12 | Internet Cache Protocol | icp_v2.c |
12 | Internet Cache Protocol | icp_v3.c |
13 | High Level Memory Pool Management | mem.c |
14 | IP Cache | ipcache.c |
15 | Neighbor Routines | neighbors.c |
16 | Cache Manager Objects | cache_manager.c |
17 | Request Forwarding | forward.c |
18 | Cache Manager Statistics | stat.c |
19 | Store Memory Primitives | stmem.c |
20 | Storage Manager | store.c |
20 | Storage Manager Client-Side Interface | store_client.c |
20 | Storage Manager Heap-Based Replacement | repl/heap/store_heap_replacement.c |
20 | Storage Manager Logging Functions | store_log.c |
20 | Storage Manager MD5 Cache Keys | store_key_md5.c |
20 | Storage Manager Swapfile Metadata | store_swapmeta.c |
20 | Storage Manager Swapin Functions | store_swapin.c |
20 | Storage Manager Swapout Functions | store_swapout.c |
20 | Store Rebuild Routines | store_rebuild.c |
21 | Misc Functions | tools.c |
22 | Refresh Calculation | refresh.c |
23 | URL Parsing | url.c |
24 | WAIS Relay | wais.c |
25 | MIME Parsing | mime.c |
26 | Secure Sockets Layer Proxy | ssl.c |
27 | Cache Announcer | send-announce.c |
28 | Access Control | acl.c |
29 | Authenticator | auth/basic/auth_basic.c |
29 | Authenticator | auth/digest/auth_digest.c |
29 | Authenticator | authenticate.c |
29 | NTLM Authenticator | auth/ntlm/auth_ntlm.c |
30 | Ident (RFC 1413) | ident.c |
31 | Hypertext Caching Protocol | htcp.c |
32 | Asynchronous Disk I/O | fs/aufs/async_io.c |
33 | Client-Side Routines | client_side.c |
34 | Dnsserver Interface | dns.c |
35 | FQDN Cache | fqdncache.c |
37 | ICMP Routines | icmp.c |
38 | Network Measurement Database | net_db.c |
39 | Cache Array Routing Protocol | carp.c |
40 | Referer Logging | referer.c |
40 | User-Agent Logging | useragent.c |
41 | Event Processing | event.c |
42 | ICMP Pinger Program | pinger.c |
43 | AIOPS | fs/aufs/aiops.c |
44 | Peer Selection Algorithm | peer_select.c |
45 | Callback Data Registry | cbdata.c |
45 | Callback Data Registry | leakfinder.c |
46 | Access Log | access_log.c |
47 | Store COSS Directory Routines | fs/coss/store_dir_coss.c |
47 | Store Directory Routines | fs/aufs/store_dir_aufs.c |
47 | Store Directory Routines | fs/diskd/store_dir_diskd.c |
47 | Store Directory Routines | fs/null/store_null.c |
47 | Store Directory Routines | fs/ufs/store_dir_ufs.c |
47 | Store Directory Routines | store_dir.c |
48 | Persistent Connections | pconn.c |
49 | SNMP Interface | snmp_agent.c |
49 | SNMP Support | snmp_core.c |
50 | Log File Handling | logfile.c |
51 | File Descriptor Functions | fd.c |
52 | URN Parsing | urn.c |
53 | AS Number Handling | asn.c |
54 | Interprocess Communication | ipc.c |
55 | HTTP Header | HttpHeader.c |
56 | HTTP Message Body | HttpBody.c |
57 | HTTP Status-Line | HttpStatusLine.c |
58 | HTTP Reply (Response) | HttpReply.c |
59 | Auto-Growing Memory Buffer with printf | MemBuf.c |
60 | Packer: A Uniform Interface to Store Like Modules | Packer.c |
61 | Redirector | redirect.c |
62 | Generic Histogram | StatHist.c |
63 | Low Level Memory Pool Management | MemPool.c |
64 | HTTP Range Header | HttpHdrRange.c |
65 | HTTP Cache Control Header | HttpHdrCc.c |
66 | HTTP Header Tools | HttpHeaderTools.c |
67 | String | String.c |
68 | HTTP Content-Range Header | HttpHdrContRange.c |
69 | HTTP Header: Extension Field | HttpHdrExtField.c |
70 | Cache Digest | CacheDigest.c |
71 | Store Digest Manager | store_digest.c |
72 | Peer Digest Routines | peer_digest.c |
73 | HTTP Request | HttpRequest.c |
74 | HTTP Message | HttpMsg.c |
75 | WHOIS Protocol | whois.c |
76 | Internal Squid Object handling | internal.c |
77 | Delay Pools | delay_pools.c |
78 | DNS Lookups; interacts with lib/rfc1035.c | dns_internal.c |
79 | Squid-Side DISKD I/O Functions | fs/diskd/store_io_diskd.c |
79 | Storage Manager COSS Interface | fs/coss/store_io_coss.c |
79 | Storage Manager UFS Interface | fs/ufs/store_io_ufs.c |
80 | WCCP Support | wccp.c |
82 | External ACL | external_acl.c |
83 | SSL Accelerator Support | ssl_support.c |
84 | Helper Process Maintenance | helper.c |
debug等级这样分配:重要消息有较低值,非重要消息有较高值。0等级是非常重要的消息,10等级是相对不紧要的消息。另外,关于等级其实并没有严格的向导或要求。开发者通常自由选择适应的debug等级。
debug_options指令决定哪个消息出现在cache.log,它的语法是:
debug_options section,level section,level ...
默认设置是ALL,1,这意味着squid会将所有等级是0或1的debug消息打印出来。假如希望cache.log里出现更少的debug消息,可设置debug_options为ALL,0。
假如想观察某个组件的其他debug信息,简单的将相应的节号和等级增加到debug_options列表的末端。例如,如下行对FTP服务端代码增加了等级5的debug:
debug_options ALL,1 9,5
如同其他配置指令一样,可以改变debug_options,然后给squid发送重置信号:
% squid -k reconfigure
注意debug_options参数是按顺序处理的,后来的值会覆盖先前的值。假如使用ALL关键字,这点尤其要注意。考虑如下示例:
debug_options 9,5 20,9 4,2 ALL,1
在该情形下,最后的值覆盖了所有先前的设置,因为ALL,1对所有节设置了debug等级为1。
选择合适的debug节号和等级有时非常困难,尤其是对squid新手而言。许多更详细的debug消息仅对squid开发者和熟悉源代码的用户有意义。无经验的squid用户会发现许多debug消息无意义和不可理解。进一步的说,假如squid相对忙的话,你可能对某个特殊请求或事件进行独立debug有困难。假如你能一次用一个请求来测试squid,那么高的debug等级通常更有用。
若以高debug等级来运行squid较长时间,需要特别谨慎。假如squid繁忙,cache.log增长非常快,并可能最终耗尽它的分区的剩余空间。假如这点发生,squid以致命消息退出。另一个关注点是性能可能下降明显。因为有大量的debug消息,squid要耗费许多CPU资源来格式化和打印字符串。将所有debug消息写往cache.log,也浪费了大量的磁盘带宽。
16.3 Coredump,断点,和堆栈跟踪
假如不幸,squid可能在运行时遭遇致命错误。这类型的错误来自3个风格:断点,总线错误,和异常分片。
断点是源代码里的正常检测。它是一个工具,被开发者用来确认在处理某事情前,相应的条件总为真。假如条件为假,程序退出并创建一个core文件,以便开发者能分析形势。如下是个典型的示例:
int some_array[100]; void some_func(int idx) { ... assert(idx < 100); some_array[idx]++; ... }
这里,断点确保数组索引的值位于数组范围内。假如去访问大于或等于100的数组元素,就会遇到错误。假如不知何故,idx的值不小于100,程序运行时会打印如下消息:
assertion failed: filename.c:123: "idx < 100"
假如这点发生在squid上,就可在cache.log里见到"assertion failed"消息。另外,操作系统会创建一个core文件,这对事后分析有用。在本节结尾,我会解释如何去处理core文件。
总线错误是:由于处理器检测到其总线上的异常条件,会引发机器语言指令执行时致命失败。当处理器试图操作非连续的内存地址时,通常会发生这种错误。在64位处理器系统上可能更容易见到总线错误,例如Alpha和某些SPARC CPU。幸运的是,它们容易修复。
异常分片错误不幸的更常见,且有时难以修复。SEGV通常发生在进程试图访问无效内存区域时(可能是个NULL指针,或超出进程空间之外的内存地址)。当bug原因和SEGV影响在不同时间呈现时,它们特别难于捕获到。
Squid默认捕获总线错误和异常分片,当它们发生时,squid试图执行一个clean shutdown(清理关闭)。可在cache.log里见到类似的语句:
FATAL: Received Bus Error...dying. 2003/09/29 23:18:01| storeDirWriteCleanLogs: Starting...
大多数情形下,squid能够写swap.state文件的clean版本。在退出前,squid调用abort()函数来创建core文件。core文件可以帮助你或其他开发者来捕获和修复bug。
在错误发生时马上创建core文件,而不是先调用clean shutdown过程,这样更利于调试。使用-C命令行选项,可以告诉squid不去捕获总线错误和异常分片:
% squid -C ...
注意某些操作系统使用文件名core,而另外一些优先考虑进程名(例如squid.core)。一旦找到core文件,请使用调试器来进行堆栈跟踪。gdb是GNU调试器--GNU C编译器的配套工具。假如没有gdb,可试着运行dbx或adb代替。如下显示如何使用gdb来进行堆栈跟踪:
% gdb /usr/local/squid/sbin/squid /path/to/squid.core ... Core was generated by 'squid'. Program terminated with signal 6, Abort trap. ...
然后,敲入where来打印堆栈轨迹:
(gdb) where #0 0x28168b54 in kill ( ) from /usr/lib/libc.so.4 #1 0x281aa0ce in abort ( ) from /usr/lib/libc.so.4 #2 0x80a2316 in death (sig=10) at tools.c:301 #3 0xbfbfffac in ?? ( ) #4 0x80abe0a in storeDiskdSend (mtype=4, sd=0x82101e0, id=1214000, sio=0x9e90a10, size=4096, offset=-1, shm_offset=0) at diskd/store_io_diskd.c:485 #5 0x80ab726 in storeDiskdWrite (SD=0x82101e0, sio=0x9e90a10, buf=0x13e94000 "...", size=4096, offset=-1, free_func=0) at diskd/store_io_diskd.c:251 #6 0x809d2fb in storeWrite (sio=0x9e90a10, buf=0x13e94000 "...", size=4096, offset=-1, free_func=0) at store_io.c:89 #7 0x80a1c2d in storeSwapOut (e=0xc5a7800) at store_swapout.c:259 #8 0x809b667 in storeAppend (e=0xc5a7800, buf=0x810f9a0 "...", len=57344) at store.c:533 #9 0x807873b in httpReadReply (fd=134, data=0xc343590) at http.c:642 #10 0x806492f in comm_poll (msec=10) at comm_select.c:445 #11 0x8084404 in main (argc=2, argv=0xbfbffa8c) at main.c:742 #12 0x804a465 in _start ( )
你可见到,堆栈轨迹打印了每个函数的名字,它的参数,以及源代码文件名和行数。当捕获bug时,这些信息特别有用。然而在某些情形下,这些还不够。可能要求你在调试器里执行其他命令,例如打印来自某个函数的变量的值:
(gdb) frame 4 #4 0x80abe0a in storeDiskdSend (mtype=4, sd=0x82101e0, id=1214000, sio=0x9e90a10, size=4096, offset=-1, shm_offset=0) at diskd/store_io_diskd.c:485 485 x = msgsnd(diskdinfo->smsgid, &M, msg_snd_rcv_sz, IPC_NOWAIT); (gdb) set print pretty (gdb) print M $2 = { mtype = 4, id = 1214000, seq_no = 7203103, callback_data = 0x9e90a10, size = 4096, offset = -1, status = -1, shm_offset = 0 }
在报告了某个bug后,请保留core文件一些天,可能还需要从它获取其他信息。
16.3.1 不能找到core文件?
core文件写在进程的当前目录。squid在启动时默认不改变其当前目录。这样你的core文件(如果有的话),会写在启动squid的目录。假如文件系统没有足够的自由空间,或进程属主没有对该目录的写权限,就无法产生core文件。可以使用coredump_dir指令来让squid使用指定的coredump目录--位于其他地方的有充足自由空间和完全权限的目录。
进程资源限制也会阻止产生core文件。进程限制参数之一是coredump文件的大小。大部分操作系统默认设置这个值为"无限"。在当前shell里使用limits或ulimit命令,可以检查当前限制。然而请注意,你的shell的限制可能不同于squid的进程限制,特别是当squid随系统启动而自动启动时。假如怀疑进程限制阻止了core文件的产生,试试这样:
csh% limit coredumpsize unlimited csh% squid -NCd1
在FreeBSD上,某个sysctl参数控制了操作系统对调用了setuid()或setgid()函数的进程,是否产生core文件。假如以root启动,squid会用到这些函数。这样为了得到coredump,必须告诉内核创建core文件,用这个命令:
# sysctl kern.sugid_coredump=1
请见sysctl.conf的manpage,关于在系统启动时如何自动设置变量的信息。
16.4 重现问题
有时候可能遇到这样的问题:某个请求,或原始服务器看起来不能与squid协调工作。可以使用下面的技术来确定问题在于squid,客户端,或原始服务器。技巧就是捕获HTTP请求,然后用不同的方法响应它,直到你验证了问题。
捕获HTTP请求意味着获取除了URL外的更多信息,包括请求方式,HTTP版本号,和所有请求头部。捕获请求的一个方法是,短期激活squid的完整debug模式。在squid主机上,敲入:
% squid -kdebug
然后,到web浏览器上发布请求。squid几乎会立刻接受到请求。在若干秒后,回到squid主机,并发布同样的命令:
% squid -kdebug
现在cache.log文件包含了上述客户端的请求。假如squid繁忙,cache.log会包含大量的请求,所以你必须查找它。它看起来如下:
2003/09/29 10:37:40| parseHttpRequest: Method is 'GET' 2003/09/29 10:37:40| parseHttpRequest: URI is 'http://squidbook.org/' 2003/09/29 10:37:40| parseHttpRequest: Client HTTP version 1.1. 2003/09/29 10:37:40| parseHttpRequest: req_hdr = { User-Agent: Mozilla/5.0 (compatible; Konqueror/3) Pragma: no-cache Cache-control: no-cache Accept: text/*, image/jpeg, image/png, image/*, */* Accept-Encoding: x-gzip, gzip, identity Accept-Charset: iso-8859-1, utf-8;q=0.5, *;q=0.5 Accept-Language: en Host: squidbook.org
注意squid把首行元素分开打印,必须手工组合它们如下:
GET http://squidbook.org/ HTTP/1.1
捕获完整请求的另一个方法是使用工具例如netcat或socket (http://www.jnickelsen.de/socket/ )。启动socket程序侦听在某个端口,然后配置浏览器使用该端口作为代理地址。当再次发起请求时,socket打印出HTTP请求:
% socket -s 8080 GET http://squidbook.org/ HTTP/1.1 User-Agent: Mozilla/5.0 (compatible; Konqueror/3) Pragma: no-cache Cache-control: no-cache Accept: text/*, image/jpeg, image/png, image/*, */* Accept-Encoding: x-gzip, gzip, identity Accept-Charset: iso-8859-1, utf-8;q=0.5, *;q=0.5 Accept-Language: en Host: squidbook.org
最后,还可以使用网络包分析工具例如tcpdump或ethereal。使用tcpdump捕获到一些包后,可以使用tcpshow来查看它们:
# tcpdump -w tcpdump.log -c 10 -s 1500 port 80 # tcpshow -noHostNames -noPortNames < tcpdump.log | less ...
Packet 4 TIME: 08:39:29.593051 (0.000627) LINK: 00:90:27:16:AA:75 -> 00:00:24:C0:0D:25 type=IP IP: 10.0.0.21 -> 206.168.0.6 hlen=20 TOS=00 dgramlen=304 id=4B29 MF/DF=0/1 frag=0 TTL=64 proto=TCP cksum=15DC TCP: port 2074 -> 80 seq=0481728885 ack=4107144217 hlen=32 (data=252) UAPRSF=011000 wnd=57920 cksum=EB38 urg=0 DATA: GET / HTTP/1.0. Host: www.ircache.net. Accept: text/html, text/plain, application/pdf, application/ postscript, text/sgml, */*;q=0.01. Accept-Encoding: gzip, compress. Accept-Language: en. Negotiate: trans. User-Agent: Lynx/2.8.1rel.2 libwww-FM/2.14..
注意tcpshow按数据里的新行字符为周期来进行打印。
一旦捕获到了某个请求,就将它存到文件。然后可以使用netcat或socket来让它重新通过squid:
% socket squidhost 3128 < request | less
假如响应看起来正常,问题可能在于用户代理。否则,可以改变不同事情来孤立问题。例如,假如你看到一些古怪的HTTP头部,那就从请求里删除它们,然后再试一次。让请求直接到达原始服务器,而不是经过squid,这样做也可调试。方法就是从请求里删除http://host.name/,并将请求发送到原始服务器:
% cat request GET / HTTP/1.1 User-Agent: Mozilla/5.0 (compatible; Konqueror/3) Pragma: no-cache Cache-control: no-cache Accept: text/*, image/jpeg, image/png, image/*, */* Accept-Encoding: x-gzip, gzip, identity Accept-Charset: iso-8859-1, utf-8;q=0.5, *;q=0.5 Accept-Language: en Host: squidbook.org % socket squidbook.org 80 < request | less
以这种方式使用HTTP时,请参考RFC 2616和Oreilly的HTTP:The Definitive Guide这本书。
16.5 报告Bug
假如你的squid版本已经有几个月未更新了,在报告bug前你应该更新它。因为其他人可能也注意了同样的bug,并且它已被修复。
假如你发现了squid的合理bug,请将它填入到squid的bug跟踪数据库:http://www.squid-cache.org/bugs/ 。 它当前是个"bugzilla"数据库,需要你创建一个帐号。当bug被squid开发者处理了时,你会接到更新通知。
假如你对报告bug很陌生,请先花时间阅读Simon Tatham写的"How to Report Bugs Effectively" (http://www.chiark.greenend.org.uk/~sgtatham/bugs.html )。
当报告bug时,确认包含下列信息:
- 1) Squid版本号。假如bug发生在不止一个版本上,就也要写上其他版本号。
- 2) 操作系统名字和版本。
- 3) bug每次都发生,还是偶尔发生。
- 4) 所发生事情的精确描述。类似于"它不能工作","请求失败"之类的语句,本质上对bug修复者无用。记得要非常详细。
- 5) 对断点,总线错误,或异常分片的堆栈跟踪。
记住squid开发者通常是无报酬的义务劳动,所以要有耐心。严重bug比小问题享有更高的解决优先级。
译后序
当译完本书最后一章时,心头袭来深深的寂寞。
在计算机领域,国内外技术水平差之甚远,部分原因归咎于语言的差异。
某种技术在国外流行若干年后,才有相应的中文文档出现。
没有文档,技术人员无法起步;而不规范的发行文档,更是误导了一批又一批的初学者。
本书的作者Duane Wessels是位大师级的人物,除了精湛的技术外,他写的本书文笔通畅,脉络清晰,丝毫不晦涩。
若对研究Squid抱着严肃的态度,那么请认真的拜读原著。
而我所能做的,只是按照我自己的理解,把原著译成中文。
好的软件只是解决了某一方面的问题,而真正可贵的,是开源的精神。
在开源的世界里,可以体会到现实中所没有的无私与奉献。
想起陈玉莲演的小龙女,和金轮法王有过这么一段对话:
金轮法王:你能接住十招,我就放过你们。
小龙女:试试看咯。
金轮法王:若接不住呢?
小龙女:接不住就接不住咯。
多年来让我记住的,是陈玉莲这种不食人间烟火,与世无争的神韵。
彭勇华
yonghua_peng@yahoo.com.cn
2005年10月于广州