先简单介绍下使用的模块
srcache-nginx-module 是nginx下的一个缓存模块,可参见官方文档 https://github.com/openresty/srcache-nginx-module
lua-resty-redis 是openresty下操作redis的模块,可参见官方文档 https://github.com/openresty/lua-resty-redis
先简单说下我要做的事情
对于CDN,大家都非常熟悉,一般用于静态资源,将真实的响应缓存在离用户最近的节点上。而我想做的就是利用srcache和redis将真实的内容缓存在redis中,这样可以大大减轻真实后端服务的压力,提高访问性能!
openresty主配置文件如下:
server { resolver 10.202.72.118 10.202.72.116; location /cache/content { internal; content_by_lua_file lua/content.lua; } location /cache/failover { internal; content_by_lua_file lua/failover.lua; } #用于清除缓存 location ~ /clearcache(/.*) { content_by_lua_file lua/clear_cache.lua; } #无缓存的PHP程序 location ~ /nocache(.*) { fastcgi_pass 127.0.0.1:9000; fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_split_path_info ^(.+\.php)(.*)$; fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info; include fastcgi.conf; } location ~ .*\.(php|php5)?$ { srcache_ignore_content_encoding on; set $cache_key ""; set $cache_expire ""; set $cache_fetch_skip 1; set $cache_store_skip 1; rewrite_by_lua_file lua/rewrite.lua; srcache_fetch_skip $cache_fetch_skip; srcache_store_skip $cache_store_skip; srcache_fetch GET /cache/content key=$cache_key; srcache_store PUT /cache/content key=$cache_key&expire=$cache_expire; add_header X-SRCache-Fetch-Status $srcache_fetch_status; add_header X-SRCache-Store-Status $srcache_store_status; #add_header srcache_fetch_skip $cache_fetch_skip; #add_header srcache_store_skip $cache_store_skip; fastcgi_pass 127.0.0.1:9000; fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_split_path_info ^(.+\.php)(.*)$; fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info; include fastcgi.conf; client_max_body_size 800m; client_body_buffer_size 128k; fastcgi_connect_timeout 3600; fastcgi_send_timeout 3600; fastcgi_read_timeout 3600; fastcgi_buffer_size 1024k; fastcgi_buffers 32 1024k; fastcgi_busy_buffers_size 2048k; fastcgi_temp_file_write_size 2048k; } location / { try_files $uri $uri/ /index.php$is_args$args; } }
rewrite.lua文件内容如下
local cache_url_list= { { ["url"] ="/cache1.php", ["expire"] ="3600", }, { ["url"] ="/cache2.php", ["expire"] ="600", }, } local request_uri_without_args = ngx.re.sub(ngx.var.request_uri, "\\?.*", "") for i, v in ipairs(cache_url_list) do if v["url"] == request_uri_without_args then local key = {ngx.var.request_method, " ",ngx.var.scheme, "://",ngx.var.host, request_uri_without_args,} local args = ngx.req.get_uri_args() query = ngx.encode_args(args) if query ~= "" then key[#key + 1] = "?" key[#key + 1] = query end key = table.concat(key) key = ngx.md5(key) ngx.var.cache_key = key ngx.var.cache_expire = v["expire"] ngx.var.cache_fetch_skip = 0 ngx.var.cache_store_skip = 0 break end end
content.lua文件内容如下:
local redis = require "resty.redis" local red = redis:new() red:set_timeout(1000) local ok, err = red:connect("192.168.1.123", 6379) if not ok then ngx.log(ngx.ERR, err) return end local count,err = red:get_reused_times() if 0 == count then ok, err = red:auth("123456") if not ok then ngx.log(ngx.ERR, err) return end elseif err then ngx.log(ngx.ERR, err) return end red:select(14) local method = ngx.req.get_method() if method == "GET" then local key = ngx.var.arg_key local res, flags, err = red:get(key) if err then ngx.log(ngx.ERR, err) ngx.exit(ngx.HTTP_SERVICE_UNAVAILABLE) end if res == nil and flags == nil and err == nil then ngx.exit(ngx.HTTP_NOT_FOUND) end ngx.print(res) elseif method == "PUT" then local value = ngx.req.get_body_data() local expire = ngx.var.arg_expire or 60 local key = ngx.var.arg_key local ok, err = red:set(key, value) if not ok then ngx.log(ngx.ERR, err) ngx.log(ngx.ERR,key..value) ngx.exit(ngx.HTTP_SERVICE_UNAVAILABLE) end red:expire(key,expire) else ngx.exit(ngx.HTTP_NOT_ALLOWED) end local ok, err = red:set_keepalive(10000, 100) if not ok then ngx.log(ngx.ERR, err) return end
failover.lua文件内容如下
ngx.header.content_type = 'text/html;charset=utf-8'; ngx.say("cache error")
clear_cache.lua文件内容如下
ngx.header.content_type = 'text/html;charset=utf-8' local redis = require "resty.redis" local red = redis:new() red:set_timeout(1000) local ok, err = red:connect("192.168.1.123", 6379) if not ok then ngx.say(err) return end local count,err = red:get_reused_times() if 0 == count then ok, err = red:auth("123456") if not ok then ngx.say(err) return end elseif err then ngx.say( err) return end red:select(14) local request_uri_without_args = ngx.re.sub(ngx.var.request_uri, "\\?.*", "") request_uri_without_args = ngx.re.gsub(request_uri_without_args,"/clearcache","") local key = {ngx.var.request_method, " ",ngx.var.scheme, "://",ngx.var.host, request_uri_without_args,} local args = ngx.req.get_uri_args() local query = ngx.encode_args(args) if query ~= "" then key[#key + 1] = "?" key[#key + 1] = query end key = table.concat(key) key = ngx.md5(key) ok,err = red:exists(key) if ok ~= 1 then ngx.say("此地址没有缓存") return end ok, err = red:del(key) if not ok then ngx.say("缓存删除失败") return end ngx.say("缓存删除成功"); ok, err = red:set_keepalive(10000, 100) if not ok then ngx.log(ngx.ERR, err) return end
这样,我们就可以通过修改rewrite.lua中cache_url_list来操作那些url需要缓存和缓存时间,要想清除缓存只要在缓存的具体url中增加/clearcache即可。
只是简单实现了其功能,还有许多需要完善的地方,不过这样的应用,对于提高接口性能应该还是效果很好的。
自己的测试如下:
nocache_1.php代码如下
<?php echo "Hello World".time(111111,6666666);
ab -c 100 -n 10000 http://127.0.0.1/nocache_1.php
访问有缓存的结果
cache1.php代码如下
<?php echo "Hello World".time(111111,6666666);
ab -c 100 -n 10000 http://127.0.0.1/cache1.php
效果还是很明显的,翻了一倍。而有缓存的结果操作了redis,redis在其他主机上,有网络IO,而无缓存PHP直接输出的,没有任何网络IO.电脑配置4核8G内存阿里云主机,centos6版本64位系统.
本方并不是比较openresty+lua和nginx+php的性能对比,而是提供一个思路,用openresty在做代理的时候,除了fastcgi_cache之外,还可能有更好方式来做些其他灵活的事情!