由于大多数的 wordpress 博客都架设在与他人共享的虚拟主机上,所以速度和优化便成了 blogger 们经久不哀的话题。为了优化,我也看过不少的文章。看来看去,很多人只是老调重弹地讲了 WP Super Cache 插件;以及有些空泛地提出要去除不必要插件、优化 javascript 等,可惜这只说明了方向问题却没有点明该如何朝这个方向去做。只好依自己生平所学,手动地折腾了一把 WordPress 优化。
服务器端缓存机制
虚拟主机用户一般无法更改服务器的配置,我们也就不能在这方面有很大的期望。不过“缓存”这一手段仍然是一种相对可行的底层优化方法。Wordpress 有众多的缓存插件来支持这一行为,包括 WP Cache、WP Super Cache、DB Cache 等。
WP Cache 和 WP Super Cache 这样的插件通过生成 HTML 静态页面来降低服务器负荷,达到提速的目的。我个人却不太喜欢这样的方式。其原因有:一,这样做会丧失动态网站的灵活性。特别是那些根据客户端不同会作出不同响应的功能。比如我的主题中有一个 is_bot() 函数,用来针对搜索引擎的机器人作出一些 SEO 的调整。一旦我启用了静态缓存插件,便丧失了这种灵活性。有人说这些功能可以改成 Javascript 实现,但真要改动起来也比较麻烦,有些得不偿失。二,某些个人博客的瓶颈并不在PHP执行这个环节,而是客户与服务器之间的网络线路。甚至我认为,个人博客没有上万的 PV 完全没有必要采用静态化的策略。
DB Cache 插件我觉得可以试试,因为它的原理是缓存数据库查询,特别是虚拟主机中数据库服务器不是本机(localhost)时,这个插件会提高响应速度。但必须注意的一点是很多虚拟主机对于每用户可占用的内存是有限制的,如果这个值太低,那么这个插件也不太适用。
把缓存交给用户
与其在服务器上费力地设置缓存,更好的办法其实是“把缓存交给用户”。我用 Firebug + Yslow 分析自己博客的时候,发现它提示我的博客没有给静态内容设置缓存。于是用 cURL 连接到网站上通过观察 HTTP Header 来分析了缓存的机制。我的博客上 Apache 会发送“Last-modified”和“E-Tag” Header,这似乎也是大多数博客虚拟主机的配置。这样浏览器在请求的时候会发出“If-modified-since”请求,让服务器判断请求的内容(比如图片)是否在某个时间(通常是浏览器缓存的时间)以后发生变化。如果没有变化,服务器返回 HTTP 304 Not Modified 响应,浏览器则可以放心地使用本地缓存,从而降低了 HTTP 请求开销。
Yslow 建议给静态内容设置一个“永久”的缓存。这个永久通常是设置一年甚至更长的缓存期来实现的。设置缓存以后,服务器在对请求作出响应的时候会附加一个 Expires Header,告诉浏览器这个东西在多长时间内不会过期。这样浏览器就可以放心地使用缓存,甚至连 If-modified-since 请求和一个 HTTP 304 响应也不必要了。这样就大大地节省了在网络上的开销。访问者只是在第一次访问时会请求动态内容,接下来则会直接使用缓存的内容,达到了“把缓存交给用户”的目的。
实现方法
要做到这个也是件很容易的事情,对于 Apache 服务器来说,使用 mod_expire 就能轻松地设置缓存期。在 .htaccess 文件中加入以下内容:
# 启用缓存机制
ExpiresActive On
# 图片缓存时间为 1 年
ExpiresByType image/gif "now plus 1 year"
ExpiresByType image/jpeg "now plus 1 year"
ExpiresByType image/x-icon "now plus 1 year"
ExpiresByType image/png "now plus 1 year"
# Javascript, CSS 缓存时间为 12 小时
ExpiresByType text/css "now plus 12 hours"
ExpiresByType text/javascript "now plus 12 hours"
ExpiresByType application/javascript "now plus 12 hours"
有人要说,如果我的内容改变了怎么办呢?因为这样设置以后浏览器并不会向服务器询问是否有新的内容,而是老老实实地相信自己的缓存内容了。
如果你的改动是少数的几个图片,那么只需要在图片的 URL 后面自己加上一个任意的 query string 即可。比如说原来的图片 URL 是
http://blog.xiaoding.org/wordpress/wp-includes/images/smilies/icon_smile.gif
现在只需要在原地址后面加上一个 query 参数即可,此参数对于静态内容可以任意构造,我此处写的是 AnyQueryString
http://blog.xiaoding.org/wordpress/wp-includes/images/smilies/icon_smile.gif?AnyQueryString
这样浏览器会认为此时的图片与原来的不同,将再一次下载它。于是我们就达到了更新的目的。
太麻烦,不干了。。。
我们在公司是通过PHP去查一下静态文件最后被修改过的时间
mtime(),貌似是这个函数,在每个静态文件后面加上一个版本号。呵呵。
@陶陶
嗯。。 只有像我这样爱折腾的人才会去搞这玩意。 😐
@simaopig
偶不太懂 PHP,改东西都是靠之前的那点 C 语言基础加上 php.net 查询函数用法…… 😯
嗯~对于图片加个过期时间确实不错~
js和css我的做法是用一个php文件集中处理,缓存压缩成gzip,同时设定过期时间。
@Bronco
我的做法是直接处理 CSS 和 JavaScript,生成 pack 过的紧凑版本,减少大小还是比较管用的
@xiaoding
js我是park+gzip,尽量压缩,CSS考虑到修改的方便性,直接gzip了。话说gzip的压缩率还是很不错的。
@Bronco
你的主机有没有CPU限制? gzip 容易导致 CPU超限的吧?
@xiaoding
我服务器上缓存了啊~每个文件只生成一次,之后的请求都直接读取gzip文件内容返回了。
这种方法唯一不太好的地方就是文件更新的话需要手动删除gzip文件。现在写了个脚本,svn更新主题的时候顺便把相应文件删掉,用着还算方便。 😎
这个方法还是蛮不错的~至少我试用了一段时间~
@LiStyle
hoho 手动的方法一般都不错,就是麻烦点,不一定有人愿意使用。
服务器允许的话,用Memcached也许是个不错的选择。
@五月殇
虚拟主机当然是不允许了,hoho
关键是 绝大多数虚拟主机都不支持mod_expire函数 国外主机同样不支持!
@chancat
怎么会呢, 至少我用过两家虚拟主机都支持。基本上你用 Linux + Apache 的都能支持,IIS 下可能会有问题。