项目背景:
当生产产生异常流量而又无法快速定位流量来源,为减少数据库负载,通过全局缓存预热,以及快速切缓存开关,来控制全站流量导入缓存,减少异常情况下对数据库的压力。
总体实现为nginx+memcache+Lua
1 首先查看一下nginx版本:
[root@squid1 sbin]# /usr/local/nginx/sbin/nginx -v
Tengine version: Tengine/2.1.2 (nginx/1.6.2)
此处nginx使用的是tengine版,nginx版本必须1.6.0以上。不是则到 http://tengine.taobao.org 下载tengine最新版本。
2 安装Lua环境
到官网http://luajit.org/download.html 下载安装包到本地并解压
tar –zxvf /usr/local/src/LuaJIT-2.0.4.tar.gz
cd LuaJIT-2.0.4
make&&make install
查看是否成功:
[root@puppetmaster LuaJIT-2.0.4]# ls /usr/local/lib
libluajit-5.1.a libluajit-5.1.so libluajit-5.1.so.2 libluajit-5.1.so.2.0.4 lua pkgconfig
[root@puppetmaster LuaJIT-2.0.4]# ls /usr/local/include/luajit-2.0
lauxlib.h luaconf.h lua.h lua.hpp luajit.h lualib.h
编辑/etc/profile文件添加环境变量:
export LUAJIT_LIB=/usr/local/lib
export LUAJIT_INC=/usr/local/include/luajit-2.0
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
3 下载nginx所需模块源码包到/usr/local/src下,并解压
https://github.com/simpl/ngx_devel_kit (nginx开发环境模块)
https://github.com/openresty/set-misc-nginx-module (nginx变量函数,算法模块)
https://github.com/openresty/srcache-nginx-module (缓存http请求,响应定制模块)
https://github.com/openresty/memc-nginx-module (memcached缓存模块)
https://github.com/openresty/lua-nginx-module (lua脚本开发框架模块)
4 tengine再编译
Tengine的dso_tool是动态加载的工具,用法为:
/usr/local/nginx/sbin/dso_tool --add-module=/usr/local/src/memc-nginx-module-master
但是并不是所有模块都可以动态加载,所以这里用再编译静态加载。
编译前查看一下现在程序的编译参数:
[root@puppetmaster sbin]# /usr/local/nginx/sbin/nginx –V
tar -zxvf /usr/local/src/tengine-2.1.2.tar.gz
cd /usr/local/src/tengine-2.1.2
在之前编译参数上再将下载的nginx模块参数添加上编译:
./configure --prefix=/usr/local/nginx --user=nginx --group=nginx
--with-http_concat_module
--with-http_addition_module
--with-http_dav_module
--with-http_gzip_static_module
--with-http_image_filter_module
--with-http_realip_module
--with-http_stub_status_module
--with-http_ssl_module
--with-http_sub_module
--with-ipv6
--with-file-aio
--with-sha1=/usr/include/openssl
--with-md5=/usr/include/openssl
--with-mail
--with-mail_ssl_module
--with-http_xslt_module
--with-http_geoip_module
--with-http_flv_module
--with-http_mp4_module
--with-http_gzip_static_module
--with-http_random_index_module
--with-http_secure_link_module
--with-http_degradation_module
--with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic'
--with-ld-opt=-Wl,-E
--add-module=/usr/local/src/ngx_devel_kit-master --add-module=/usr/local/src/set-misc-nginx-module-master --add-module=/usr/local/src/srcache-nginx-module-master --add-module=/usr/local/src/memc-nginx-module-master --add-module=/usr/local/src/lua-nginx-module-master
make
make install
tengine的再编译并不会将原来自建的配置文件如nginx.conf删除。
可以查看一下是否已将模块编译进去
[root@puppetmaster sbin]# /usr/local/nginx/sbin/nginx –V
5 将lua源码并上传到nginx服务器
/data/nfsroot/client/ngx_lua/
6 新建nginx的配置文件cache.conf用于nginx调用各个模块
Vim /usr/local/nginx/conf/cache.conf
set $cache_fetch_skip 1;
set $cache_store_skip 1;
set $cmd_key $host$request_uri;
srcache_response_cache_control off;
rewrite_by_lua_file /data/nfsroot/client/ngx_lua/cache_config.lua;
srcache_methods GET; #GET POST
srcache_fetch_skip $cache_fetch_skip;
srcache_store_skip $cache_store_skip;
srcache_store_statuses 200 201 301 302; #200 201 301 302 500
srcache_fetch GET /memc $cmd_key;
srcache_store PUT /memc $cmd_key;
add_header X-Cached-From $srcache_fetch_status;
7 修改配置文件nginx.cof
添加upstream,主机为memcache服务器
upstream memc_server1 {
server 192.168.0.1:12000;
keepalive 512;
}
upstream memc_server2 {
server 192.168.0.2:12000;
keepalive 512;
}
upstream_list memc_servers memc_server1 memc_server2;
在所需要的虚拟主机下添加:如www.hello.com
location /memc {
#允许内网访问
internal;
memc_connect_timeout 100ms;
memc_send_timeout 100ms;
memc_read_timeout 100ms;
set $memc_key $host$request_uri;
#一致性hash算法和java客户端类似
set_hashed_upstream $memc_backends memc_servers $memc_key;
#设置缓存时间 10分钟
set $memc_exptime 600;
memc_pass $memc_backends;
}
在所需要缓存的应用下添加配置文件路径,如:
location /local/{
include /usr/local/nginx/conf/cache.conf;
proxy_pass http://hello_servers/hello/route/;
}
8 pkill nginx
/usr/local/nginx/sbin/nginx -c /etc/nginx/conf/nginx.conf
主要原理:
通过lua 脚本控制缓存的存开关和取开关
/data/nfsroot/client/ngx_lua/cache_config.lua;
ngx.var.cache_fetch_skip=0 标示抓取缓存
ngx.var.cache_store_skip=0 标示缓存被代理的缓存
ngx.var.cache_fetch_skip=1 跳过GET缓存
ngx.var.cache_store_skip=1 跳过PUT缓存
一般我们只需要设置成这样既可
ngx.var.cache_fetch_skip=1 标示抓取缓存 需要的时候 修改为0 reload nginx 全站缓存开启,抵御流量
ngx.var.cache_store_skip=0 预热到缓存
附lua源码
[root@squid1 client]# tree ngx_lua
ngx_lua
├── cache_config.lua
├── module
│ ├── limit_share.lua
│ ├── mySqlClient.lua
│ ├── redisClient.lua
│ └── share.lua
├── README.md
└── test.lua
cache_config.lua
1 ngx.var.cache_fetch_skip=1 2 ngx.var.cache_store_skip=0
limit_share.lua
1 local _M = {} 2 local lrucache = require "lrucache" 3 local c = lrucache.new(600) -- allow up to 200 items in the cache 4 if not c then 5 return error("failed to create the cache: " .. (err or "unknown")) 6 end 7 --开启全站缓存 8 c:set("full_cache",0) 9 c:set("CACHE_POST",0) 10 function _M.go() 11 c:set("dog", 32) 12 c:set("cat", 56) 13 -- ngx.say("dog: ", c:get("dog")) 14 -- ngx.say("cat: ", c:get("cat")) 15 16 -- c:set("dog", { age = 10 }, 0.1) -- expire in 0.1 sec 17 -- c:delete("dog") 18 end 19 20 function _M.getC() 21 return c 22 end 23 24 return _M
mySqlClient.lua
redisClient.lua
1 local redis = require("redis") 2 local _Redis ={} 3 function _Redis:client() 4 5 local red = redis:new() 6 7 red:set_timeout(1000) -- 1 sec 8 9 10 local ok, err = red:connect("192.168.59.103", 6379) 11 if not ok then 12 ngx.say("failed to connect to redis: ", err) 13 else 14 ngx.ctx.redis=red 15 end 16 17 return ngx.ctx.redis 18 end 19 20 --- 关闭连接 21 function _Redis:close() 22 if ngx.ctx.redis then 23 ngx.ctx.redis:set_keepalive(10000, 100) 24 ngx.ctx.redis = nil 25 end 26 end 27 28 return _Redis
share.lua
1 local _M = {} 2 local lrucache = require "lrucache" 3 local c = lrucache.new(200) -- allow up to 200 items in the cache 4 if not c then 5 return error("failed to create the cache: " .. (err or "unknown")) 6 end 7 8 function _M.go() 9 10 -- ngx.say("dog: ", c:get("dog")) 11 -- ngx.say("cat: ", c:get("cat")) 12 13 -- c:set("dog", { age = 10 }, 0.1) -- expire in 0.1 sec 14 -- c:delete("dog") 15 end 16 17 function _M.check(args) 18 ngx.say(args) 19 return c:get(args) 20 end 21 22 function _M.getC() 23 if ngx.ctx.c then 24 return ngx.ctx.c 25 end 26 27 ngx.ctx.c=c 28 return ngx.ctx.c 29 end 30 31 return _M