Nginx+Memcache实现静态化缓存

Posted by jintang on 2014-10-22

Nginx实现静态化,会通过路由访问到静态资源服务器中,读取静态文件,如果没有则由后端程序生成后写入磁盘,这是一个简单的场景。

虽说静态文件访问非常快,是仍然会产生一定的I/O读写。到这你可能会想,nginx不是可以开启open_file_cache吗?

1
2
3
4
open_file_cache max=100000 inactive=20s;  
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;

open_file_cache 打开缓存的同时也指定了缓存最大数目,以及缓存的时间。我们可以设置一个相对高的最大时间,这样我们可以在它们不活动超过20秒后清除掉。

这也是一种减少I/O读写的办法吧,但只是一时,而且这种缓存的失效并不可控,如果资源更新了,只能等缓存失效。

使用Memcache来缓存页面

如果用memcache来做缓存,大家一定想到这样的一个memcache的整合方式:

这很容易理解,后端程序负责页面逻辑,输出到memcache中缓存起来,下一次访问时,可以经过一定的缓存策略判断是否失效,有效则返回。 好处就是不仅可以不打开文件,不用不走磁盘I/O,而且缓存可控。

但是存在一个问题就是没有做到动静分离,用户访问静态资源,事实上仍然走的一层后端程序,需要判断缓存是否失效。

Nginx直接连接Memcache

终于来到重点了,其实如果查询缓存直接交给了Nginx,只有当缓存失效或不存在时,再由后端程序经过业务逻辑后返回,同时输出一份到memcache缓存起来。
下一次访问时就不用走后端程序了,Nginx直接查询memcache获得内容。而静态资源的缓存也是由后端程序决定的,做到了缓存策略可控。

下面就来一步步实现:

Nginx编译安装

nginx安装不多说了,关键是需要安装好对应的模块,包括:

1
2
ngx_http_memcached_module
ngx_http_consistent_hash

ngx_http_memcached_module 是用于nginx连接memcache支持,事实上nginx在2005年的0.3.12版本就默认带上这个模块了,所以是不必单独安装。
ngx_http_consistent_hash 用于负载均衡的调度策略,具体用来做什么,下文会介绍,这里先安装好

这里使用的是nginx-1.4.7版本

我们需要安装的是ngx_http_consistent_hash模块,下载地址:https://github.com/replay/ngx_http_consistent_hash

1
2
cd /usr/local/src
git clone https://github.com/replay/ngx_http_consistent_hash.git
1
2
3
4
5
6
7
8
wget http://nginx.org/download/nginx-1.4.7.tar.gz
tar -xf nginx-1.4.7
cd nginx-1.4.7

# 注意要添加上对应的模块路径: --add-module=/path/to/module
./configure --prefix=/usr/local/nginx --add-module=/usr/local/src/nginx_http_consistent_hash
make
make install

如果是已经安装好,只需要重新编译nginx,然后进行热部署即可
热部署参考另一篇文章:Nginx如何进行热升级部署

Nginx配置支持访问memcache

环境如下:

1
2
3
4
Nginx: 192.168.1.100
Memcache Server 1: 192.168.1.101
Memcache Server 2: 192.168.1.102
PHP-FPM Server: 192.168.1.103

这里memcache使用11211默认端口, php-fpm使用9000端口

添加memcache负载集群
1
2
3
4
upstream memcache_server {
server 192.168.1.101:11211 weight=1;
server 192.168.1.102:11212 weight=1;
}

添加路由

1
2
3
4
5
6
7
8
9
10
location ~ \.(js|css|html) { # 此处针对的是html,具体路由方式,根据业务需要

# 用$request_uri作为key,相当于访问"xxx.html"当做Key
set $memcached_key $request_uri;

memcache_pass memcache_server;

# 当找不到对应的key-value,则返回404, 然后回调到后端处理,这一步比较关键
error_page 404 /callback.php;
}

添加后端程序路由

1
2
3
4
5
6
location ~ \.php$ {
fastcgi_pass 192.168.1.103:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
后端回调处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
# callback.php

$uri = $_SERVER['REQUEST_URI'];

$memcache = new Memcache();
$memcache->addServer('192.168.1.101', 11211);
$memcache->addServer('192.168.1.102', 11211);

$html = ''; // html根据逻辑而定

// 建立缓存
$memcache->add($uri, $html);

// 返回页面内容
echo $html;

当nginx访问memcache没有找到对应的内容时,才交给后端callback处理。从callback看出,后端从新建立缓存到memcache。

关于调度策略

上面upstream memcache_server {…}负载配置,我们使用加权轮询的策略。当集群数量越来越多,添加机器和有机器宕机时,就容易产生大批量的数据迁移。
这时就可以使用一致性哈希环来解决。ngx_http_consistent_hash模块就是用来提供这样的调度方式。所以上面memcache集群负载配置就需要增加一行:

1
2
3
4
5
upstream memcache_server {
consistent_hash $request_uri; # 指明使用consistent_hash调度策略
server 192.168.1.101:11211 weight=1;
server 192.168.1.102:11212 weight=1;
}

相应地,在callback.php使用memcache集群时,也需要通用的调度策略,可以通过调整php.ini中memcache模块的配置来设置。

1
2
3
4
[memcache]

memcache.hash_strategy consistent
# 控制key到服务器的映射(分布式)策略。

consistent 允许服务器增减而不会(大量)导致健的重新映射 (译注:参见http://tech.idv2.com/2008/07/24/memcached-004/)
standard 则使用余数方式进行key的映射。