http://blog.csdn.net/21aspnet/article/details/6584793
也许你还在为刚才动态内容获得7336.76 reqs/s的吞吐率感到振奋,等等,理想和现实是有差距的,你要忍受现实的残酷,别忘了,我们压力测试中的动态内容都处于全缓存情况下,也就是每次请求都命中缓存,这在现实中往往是不可能的。
首先,缓存区空间大小是有限的,而我们的站点可能有大量的内容需要被缓存,而不像前边压力测试时只有一个内容。一旦缓存区被装满,那么缓存管理器便会淘汰 一些它认为不再需要的缓存内容,比如通过LRU(最近最少使用算法)将使用频率较低的缓存内容淘汰出去,但是,这里判断“不常使用”的标准是不严格的,也 许被淘汰的内容就是你将要访问的下一个内容,这便影响了它的命中率。
其次,缓存的过期时间也影响到它的命中率,假如有效期很短,为10秒,那么最少10秒便会有一次无法命中。
还有,有些内容可能根本没有被代理服务器缓存,比如这些内容包含了set-cookie等不可缓存的HTTP头信息,导致反向代理不会缓存它们,并且在浏览器请求它们的时候也不会去缓存区查找。这是影响命中率的一个重要因素,但往往被我们所忽略。
幸运的是,这些问题我们都可以轻松的解决,前提是,我们需要了解反向代理缓存的实时工作状态,比如Varnish便提供了一个命令行的状态监控程序varnishstat,我们打开它,便看到了当前时刻的状态,如下:
- client_conn 9908723 94.05 Client connections accepted
- client_drop 0 0.00 Connection dropped, no sess/wrk
- client_req 16433490 155.99 Client requests received
- cache_hit 8751732 83.07 Cache hits
- cache_hitpass 42592 0.40 Cache hits for pass
- cache_miss 7573389 71.89 Cache misses
- backend_conn 3889845 36.92 Backend conn. success
- backend_unhealthy 220 0.00 Backend conn. not attempted
- backend_busy 0 0.00 Backend conn. too many
- backend_fail 4536 0.04 Backend conn. failures
- backend_reuse 3780212 35.88 Backend conn. reuses
- backend_toolate 3866687 36.70 Backend conn. was closed
- backend_recycle 7646677 72.58 Backend conn. recycles
- backend_unused 0 0.00 Backend conn. unused
- fetch_head 57 0.00 Fetch head
- fetch_length 155097 1.47 Fetch with Length
- fetch_chunked 7508522 71.27 Fetch chunked
- fetch_eof 0 0.00 Fetch EOF
- fetch_bad 0 0.00 Fetch had bad headers
- fetch_close 3982 0.04 Fetch wanted close
- fetch_oldhttp 0 0.00 Fetch pre HTTP/1.1 closed
- fetch_zero 0 0.00 Fetch zero len
- fetch_failed 0 0.00 Fetch failed
- n_sess_mem 1033 . N struct sess_mem
- n_sess 633 . N struct sess
- n_object 1016443 . N struct object
- n_vampireobject 0 . N unresurrected objects
- n_objectcore 1017564 . N struct objectcore
- n_objecthead 982903 . N struct objecthead
- n_smf 2647421 . N struct smf
- n_smf_frag 622470 . N small free smf
- n_smf_large 3 . N large free smf
- n_vbe_conn 12 . N struct vbe_conn
- n_wrk 8000 . N worker threads
- n_wrk_create 8000 0.08 N worker threads created
- n_wrk_failed 0 0.00 N worker threads not created
- n_wrk_max 11021 0.10 N worker threads limited
- n_wrk_queue 0 0.00 N queued work requests
- n_wrk_overflow 2441 0.02 N overflowed work requests
- n_wrk_drop 0 0.00 N dropped work requests
- n_backend 4 . N backends
- n_expired 6344546 . N expired objects
- n_lru_nuked 183957 . N LRU nuked objects
- n_lru_saved 0 . N LRU saved objects
- n_lru_moved 3692170 . N LRU moved objects
- n_deathrow 0 . N objects on deathrow
- losthdr 84 0.00 HTTP header overflows
- n_objsendfile 0 0.00 Objects sent with sendfile
- n_objwrite 15466812 146.81 Objects sent with write
- n_objoverflow 0 0.00 Objects overflowing workspace
- s_sess 9906155 94.03 Total Sessions
- s_req 16433490 155.99 Total Requests
- s_pipe 37 0.00 Total pipe
- s_pass 108252 1.03 Total pass
- s_fetch 7667658 72.78 Total fetch
- s_hdrbytes 7187255662 68221.35 Total header bytes
- s_bodybytes 111592032839 1059230.32 Total body bytes
- sess_closed 1905544 18.09 Session Closed
- sess_pipeline 0 0.00 Session Pipeline
- sess_readahead 0 0.00 Session Read Ahead
- sess_linger 15277717 145.02 Session Linger
- sess_herd 13547370 128.59 Session herd
- shm_records 1028855796 9765.89 SHM records
- shm_writes 77957008 739.97 SHM writes
- shm_flushes 131005 1.24 SHM flushes due to overflow
- shm_cont 144281 1.37 SHM MTX contention
- shm_cycles 427 0.00 SHM cycles through buffer
- sm_nreq 15306717 145.29 allocator requests
- sm_nobj 2024948 . outstanding allocations
- sm_balloc 13595295744 . bytes allocated
- sm_bfree 40091795456 . bytes free
- sma_nreq 0 0.00 SMA allocator requests
- sma_nobj 0 . SMA outstanding allocations
- sma_nbytes 0 . SMA outstanding bytes
- sma_balloc 0 . SMA bytes allocated
- sma_bfree 0 . SMA bytes free
- sms_nreq 14062 0.13 SMS allocator requests
- sms_nobj 0 . SMS outstanding allocations
- sms_nbytes 487 . SMS outstanding bytes
- sms_balloc 6844837 . SMS bytes allocated
- sms_bfree 6846298 . SMS bytes freed
- backend_req 7668789 72.79 Backend requests made
- n_vcl 1 0.00 N vcl total
- n_vcl_avail 1 0.00 N vcl available
- n_vcl_discard 0 0.00 N vcl discarded
- n_purge 740577 . N total active purges
- n_purge_add 905392 8.59 N new purges added
- n_purge_retire 164815 1.56 N old purges deleted
- n_purge_obj_test 13397373 127.17 N objects tested
- n_purge_re_test 131875330367 1251759.15 N regexps tested against
- n_purge_dups 240655 2.28 N duplicate purges removed
- hcb_nolock 0 0.00 HCB Lookups without lock
- hcb_lock 0 0.00 HCB Lookups with lock
- hcb_insert 0 0.00 HCB Inserts
- esi_parse 0 0.00 Objects ESI parsed (unlock)
- esi_errors 0 0.00 ESI parse errors (unlock)
- accept_fail 2553 0.02 Accept failures
- client_drop_late 0 0.00 Connection dropped late
- uptime 105352 1.00 Client uptime
看起来很强大,的确,它帮我们获得了很多重要的信息,包括了三个重要的统计项:
N struct object
当前被cache的条目
Client requests received
代表到目前为止,浏览器向反向代理服务器发送的HTTP请求累积次数,由于可能使用长连接,所以它可能会大于上边的Client connections accepted。
Cache hits
代表在这些请求中,反向代理服务器在缓存区中查找并且命中缓存的次数。
Cache misses
代表在这些请求中,反向代理服务器在缓存区中查找但是没有命中缓存的次数。
N expired objects
代表过期的缓存内容个数
N LRU nuked objects
由于cache空间满而不得不扔掉的cache条目,如果这个数字是0,就没必要增加cache的大小了。
N LRU moved objects
代表被淘汰的缓存内容个数
Total header bytes
代表缓存区中所有缓存内容的HTTP头信息长度
Total body bytes
代表缓存区中所有缓存内容的正文长度
通过以上这些统计项,我们还可以知道更多,比如Cache hits和Cache misses的总和代表了反向代理服务器在缓存区中查找内容的总次数;而用Client requests received减去这个总次数,便大概等于没有查找缓存的请求数;Total body bytes和Total body bytes的总和则代表了缓存区当前的空间使用量。
了解了这些后,我们回头看前边的三个问题,都很容易分析和解决。
对于缓存区空间大小的问题,我们可以通过监视它的使用量以及缓存淘汰个数,在必要的时候分配更多的缓存区空间,但是这可能需要重新启动反向代理服务器。当 然,我们应该首先根据站点的规模,来估算出大概的缓存区空间,并且分配出大约为它5倍以上的缓存区空间,以适应一段时期内的内容数量增长。
其次,是缓存有效期取值的问题,但这本身不是一个问题,它取决于后端Web服务器的承受能力,以及你对站点内容实时性的要求,我们在前边介绍动态内容缓存 的章节中曾经探讨过缓存有效期的取值,实质上是一个寻找“过”与“不及”之间平衡的长期过程,但在动态程序实现的内容缓存机制中,我们还可以通过主动删除 缓存的方法来实现缓存到期之前的更新,而基于HTTP协议的反向代理缓存机制则不容易做到这一点,后端的动态程序无法主动删除某个缓存内容,除非我们清空 反向代理服务器上的缓存区。
在寻找平衡的过程中,有时候实时性需求可能要向缓存有效期适当让步,当我们的站点面临较大的压力时,我们不得不暂时以牺牲实时性作为代价,适当的延长缓存有效期,直到我们有了成熟的性能扩展方案。
在初步评估缓存有效期的时候,我们可以通过一些量化的计算来得出理论数值,举个例子,假如我们的站点有60000个动态内容处于频繁访问的状态,我们通过 Cache-Control将它们在反向代理服务器上的缓存有效期都设置为60秒,这样一样来,后端服务器将必须承受最多每秒处理1000个动态内容的工 作量,如果这些动态内容都进行完整的计算,比如访问数据库,那么显然后端服务器是无法承受的,这时候我们可以将缓存有效期延长到300秒,即5分钟,这使 得后端服务器只需要每秒处理200个动态内容,大大减少了工作量。
那么,如果从缓存命中率的角度来分析缓存有效期取值的时候,有一点需要明白,当我们看到缓存的命中率非常低时,有时并不代表我们需要马上延长缓存有效期, 这时候还要观察当前的站点实际吞吐率,举个极端的例子,假如你发现你的站点1个小时内只有1次访问,而缓存有效期为半个小时,那缓存命中率必然会非常低, 但即便是每次缓存都不命中,也不会对后端带来什么压力,实际上如果你的站点只有一台Web服务器,并且站点的实际吞吐率远远低于它的处理能力时,完全没有 必要使用反向代理服务器。
最后关于内容没有被缓存的问题,我们也可以在Varnish的状态监视中找到线索,比如有一个用Varnish加速第三方应用wordpress的例子,得到的Varnish状态如下:
- 4+10:37:44
- my.server.com
- Hitrate ratio: 1 1 1
- Hitrate avg: 0.0364 0.0364 0.0364
- 2324 0.00 0.01 Client connections accepted
- 6191 0.00 0.02 Client requests received
- 12 0.00 0.00 Cache hits
- 7 0.00 0.00 Cache hits for pass
- 318 0.00 0.00 Cache misses
- 6179 0.00 0.02 Backend connections success
- 0 0.00 0.00 Backend connections not
- attempted
- 0 0.00 0.00 Backend connections too many
- 0 0.00 0.00 Backend connections failures
- 4057 0.00 0.01 Backend connections reuses
- 6151 0.00 0.02 Backend connections recycles
- ...
我们看到,Varnish一共处理了来自浏览器的6191个请求,其中命中缓存的有12个,真是太少了,而没有命中缓存的有318个,奇怪,剩下的 那么多请求根本就没有去缓存区检查,也就是说,Varnish认为那些内容不能被缓存。不能被缓存总是有原因的,你需要根据反向代理缓存的规则,来进一步 的检查。而我们这个例子中,都是cookies惹得祸,因为在wordpress中由于我们会安装一些各种各样的插件,有些插件会使得wordpress 的每个页面都带有写入cookies的set-cookie标记,而我们前边在vcl_fetch函数中禁止了这类内容的缓存,问题就在这里了,我们希望 将这些多余的cookie关闭掉,但是,wordpress自身的登录和管理页面是需要cookies才可以正常工作的,所以我们还需要让反向代理不缓存 这些页面,我们使用以下VCL配置:
- sub vcl_recv
- {
- if (!(req.url ~ "wp-(login|admin)"))
- {
- unset req.http.cookie;
- }
- }
- sub vcl_fetch
- {
- if (!(req.url ~ "wp-(login|admin)"))
- {
- unset obj.http.set-cookie;
- }
- }
可以看到,除了wordpress自身的登录和管理页面以外,我们将其它内容的HTTP头信息中有关cookie的标记全部都清除掉,这使得Varnish可以将大部分内容缓存起来,提高缓存命中率,同时不影响我们登录和管理wordpress。
这个例子给了我们一些启示,对于我们自己的站点,如果从长远考虑,那么在规划的时候就要费点心思,我们可以根据内容是否可以缓存在反向代理服务器上,将它们置于不同的主机,这样便可以在必要的时候将可以缓存的内容快速与反向代理服务器对接,获得较好的加速效果。