openresty–redis–srcache缓存的应用

先简单介绍下使用的模块

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之外,还可能有更好方式来做些其他灵活的事情!

此条目发表在 好文推荐, 网站架构 分类目录,贴了 , 标签。将固定链接加入收藏夹。

评论功能已关闭。