折腾搬瓦工–12–继续优化WordPress
Overview
前言
上一篇文章里谈到了使用七牛CDN加速WordPress,但CDN无法解决部分静态资源如css以及字体地址被屏蔽的问题,于是后面直接在WordPress的主题目录下执行
1grep -rn "fonts.googleapis.com" *
2grep -rn "fonts.gstatic.com" *
找到所有包含fonts.gstatic.com以及fonts.googleapis.com链接的文件,下载对应的CSS与字体到服务器上,并将这些链接逐一替换,这样可以保证站点不被屏蔽的情况下,即使速度稍慢,也能完整加载,问题是不能经常变更主题,不然逐一分析替换所有无法加载的资源(通常都是谷歌提供的资源)也相当麻烦。
然后启用Nginx的静态资源缓存策略,将js、css、字体、图片等静态资源的过期时间设为永久,访问了首页后继续访问其他页面的流量消耗只包含动态资源以及未缓存的静态资源,后台页面也会更流畅。
最后配置HTTPS并启用HTTP2,首部压缩,服务端推送,多路复用等特性可以让静态资源并发加载同时减少流量消耗,而升级到PHP7或者使用HHVM也能够提升动态页面的响应速度,剩下的唯一问题就是选一个好的服务提供商了。
到现在未尝试过的优化,还剩下启用mircocache,openVZ平台启用lkl-bbr,使用固定链接静态化博客,这次会尝试下这些优化策略。
启用固定链接与缓存
Nginx官方教程里的microcache以及适配wp-super-cache都要求使用固定链接,对POST请求、带查询参数的GET请求、后台页面等不缓存,只有不带参数的GET请求结果缓存在内存或者硬盘上,后续请求命中缓存后直接返回,不需要再次查询数据库和渲染页面。由于这个博客一直没有启用固定链接,文章的链接都是查询格式的,最初microcahe使用起来效果不明显,通过Chrome调试工具发现主要时间都耗在网络传输上。
Nginx官方博客的教程链接:9-tips-for-improving-wordpress-performance-with-nginx
理论上从响应时间看,内存静态内容>硬盘静态内容>动态内容,前两者之间的差距在请求量小的情况下不是很明显,microcache直接根据请求的类型缓存,而使用wp-super-cache插件的灵活性更高,可以在后台创建更详细的缓存策略,两种都尝试之后,还是选择了使用wp-super-cache。
首先需要启用固定链接,在WordPress的后台设置中可以开启,我的固定链接格式如图:
更适合SEO的格式是链接中包含文章标题,如上面给出的Nginx官方教程链接,使用英文和横杠分割单词,有利于收录和检索,修改后注意做好访问重定向,避免旧的链接出现404的情况
然后安装wp-super-cache插件,启用缓存,最傻瓜的方式是勾选所有推荐的选项,然后测试是否可行。缓存的页面文件默认在**/wp-content/cache**目录下,我启用了预缓存,静态化所有文章,压缩页面以及移动设备支持,可以看到缓存目录下包含了所有的站点内容。
最后配置Nginx,让符合条件的请求首先匹配硬盘上的文件,文件不存在时再转发给FastCGI
1set $cache_uri $request_uri;
2
3# POST requests and URLs with a query string should always go to PHP
4if ($request_method = POST) {
5 set $cache_uri 'null cache';
6}
7if ($query_string != "") {
8 set $cache_uri 'null cache';
9}
10
11# Don't cache URIs containing the following segments
12if ($request_uri ~* "(/wp-admin/|/xmlrpc.php|/wp-(app|cron|login|register|mail).php
13 |wp-.*.php|/feed/|index.php|wp-comments-popup.php
14 |wp-links-opml.php|wp-locations.php |sitemap(_index)?.xml
15 |[a-z0-9_-]+-sitemap([0-9]+)?.xml)") {
16
17 set $cache_uri 'null cache';
18}
19
20# Don't use the cache for logged-in users or recent commenters
21if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+
22 |wp-postpass|wordpress_logged_in") {
23 set $cache_uri 'null cache';
24}
25
26# Use cached or actual file if it exists, otherwise pass request to WordPress
27location / {
28 try_files /wp-content/cache/supercache/$http_host/$cache_uri/index.html
29 $uri $uri/ /index.php;
30}
31location ~ .php$ {
32 try_files $uri /index.php;
33 include fastcgi.conf;
34 fastcgi_pass unix:/var/run/php7.2-fpm.sock;
35 proxy_set_header Host $host;
36 proxy_set_header X-Real-IP $remote_addr;
37}
启用BBR
KVM虚拟化的主机可以安装版本4.10及以上的内核,为整个系统启用BBR,而在openVZ平台上,只能使用lkl-bbr为指定程序启用bbr,由于一直无法为nginx启用bbr,只能改为先为haproxy启用bbr,再将请求转发给nginx。网上的教程没有解决这种情况下nginx收到的请求源地址都是内网地址的问题,但通过在haproxy中启用send-proxy和在nginx中启用proxy_protocol后,可以获取到请求的源地址。
成功开启后,对于插图较多的文章,加载速度会有比较大提升,由于多了一层转发,在全站开启https的情况下,wp-super-cache测试缓存时会报ssl相关的错误,需要进一步了解haproxy做负载均衡情况下网络请求的转发流程,目前暂时不启用,但还是记录一下。
首先安装haproxy,然后修改service文件,让haproxy直接读取配置文件并运行,修改后 /lib/systemd/system/haproxy.service 文件内容如下
1[Unit]
2Description=HAProxy Load Balancer
3Documentation=man:haproxy(1)
4Documentation=file:/usr/share/doc/haproxy/configuration.txt.gz
5After=network.target syslog.service
6Wants=syslog.service
7
8[Service]
9Environment=CONFIG=/etc/haproxy/haproxy.cfg
10EnvironmentFile=-/etc/default/haproxy
11ExecStart=/usr/sbin/haproxy -f ${CONFIG} -p /run/haproxy.pid $EXTRAOPTS
12ExecReload=/usr/sbin/haproxy -c -f ${CONFIG}
13ExecReload=/bin/kill -USR2 $MAINPID
14KillMode=mixed
15Restart=always
16
17[Install]
18WantedBy=multi-user.target
其次,修改**/etc/haproxy/haproxy.cfg**文件,配置监听端口与转发目标,注意设置send-proxy
1global
2log 127.0.0.1 local0 notice
3maxconn 2000
4user haproxy
5group haproxy
6
7defaults
8log global
9mode tcp
10balance leastconn
11option dontlognull
12option tcplog
13option tcpka
14timeout connect 5000
15timeout client 10000
16timeout server 10000
17
18frontend proxy-in-80
19bind *:80
20default_backend proxy-out
21
22frontend proxy-in-443
23bind *:443
24default_backend proxy-out
25
26backend proxy-out
27server nginx 10.2.0.1 maxconn 20480 send-proxy
再次,修改**/etc/default/haproxy**文件,配置lkl相关环境变量
1LD_PRELOAD=/etc/liblkl-hijack.so
2LKL_HIJACK_NET_QDISC="root|fq"
3LKL_HIJACK_SYSCTL="net.ipv4.tcp_fastopen=3;net.ipv4.tcp_congestion_control=bbr;net.ipv4.tcp_wmem=4096 65536 67108864"
4LKL_HIJACK_OFFLOAD="0x9983"
5LKL_HIJACK_NET_IFTYPE=tap
6LKL_HIJACK_NET_IFPARAMS=nginx-tap
7LKL_HIJACK_NET_IP=10.2.0.3
8LKL_HIJACK_NET_NETMASK_LEN=24
9LKL_HIJACK_NET_GATEWAY=10.2.0.1
10LKL_HIJACK_DEBUG=all
同时执行以下命令,创建一个新的tap,并设置iptables转发规则
1ip tuntap add nginx-tap mode tap
2ip addr add 10.2.0.1/24 dev nginx-tap
3ip link set nginx-tap up
4
5iptables -t nat -A PREROUTING -i venet0 -p tcp -m tcp --dport 80 -j DNAT --to-destination 10.2.0.3
6iptables -t nat -A PREROUTING -i venet0 -p tcp -m tcp --dport 443 -j DNAT --to-destination 10.2.0.3
7iptables -t nat<strong> </strong>-A POSTROUTING -o venet0 -j MASQUERADE
最后修改nginx配置文件,设置proxy_protocol,并从指定目标获取原始请求的IP,并在转发给后端时设置源IP
1server {
2
3 ......
4
5 listen 443 ssl http2 proxy_protocol;
6
7 listen [::]:443 ssl http2 proxy_protocol;
8
9 ......
10
11 set_real_ip_from 10.2.0.3;
12 real_ip_header proxy_protocol;
13
14 ....
15 location / {
16 ...
17 proxy_set_header Host $host;
18 proxy_set_header X-Real-IP $proxy_protocol_addr;
19 proxy_set_header X-Forwarded-For $proxy_protocol_addr;
20 }
21}
最后重启haproxy与Nginx
Update 2018.11.26
关于proxy protocol可以在haproxy的文档中找到答案:proxy-protocol
关于nginx配置从proxy protocol获取源IP,也可以在文档中找到答案:Accepting the PROXY Protocol
使用lkl-bbr的基本思路是拦截系统调用,替换拥塞调度算法,理论上所有使用glibc的程序都可以被拦截,而像Go这样自行处理系统调用的就无法被正常拦截了,除非使用Cgo,除此之外:
(1)haproxy可以被正常拦截,而nginx不行,所以我们使用了haproxy作为nginx的前端
(2)haproxy未配置proxy protocol时,默认向后端发送的是绑定的内网IP(10.2.0.3),proxy protocol是haproxy定义的用于传递源IP的协议
(3)nginx支持proxy protocol,但需要手动配置,从haproxy转发的请求中解出源IP
(4)ngx_http_realip_module可以用来改变客户端地址,上面的配置中就是用它从proxy protocol中解出源IP,设置并转发给后端