Nginx缓存解决方案:SRCache
- - 火丁笔记前些天帮别人优化PHP程序,搞得灰头土脸,最后黔驴技穷开启了 FastCGI Cache,算是勉强应付过去了吧. 不过FastCGI Cache不支持分布式缓存,当服务器很多的时候,冗余的浪费将非常严重,此外还有数据一致性问题,所以它只是一个粗线条的解决方案. 对此类问题而言, SRCache是一个细粒度的解决方案.
前些天帮别人优化PHP程序,搞得灰头土脸,最后黔驴技穷开启了 FastCGI Cache,算是勉强应付过去了吧。不过FastCGI Cache不支持分布式缓存,当服务器很多的时候,冗余的浪费将非常严重,此外还有数据一致性问题,所以它只是一个粗线条的解决方案。
对此类问题而言, SRCache是一个细粒度的解决方案。其工作原理大致如下:
当问题比较简单的时候,通常SRCache和 Memc模块一起搭配使用。网上能搜索到一些相关的 例子,大家可以参考,这里就不赘述了。当问题比较复杂的时候,比如说缓存键的动态计算等,就不得不写一点代码了,此时 Lua模块是最佳选择。
闲言碎语不多讲,表一表Nginx配置文件长啥样:
lua_package_path '/path/to/vendor/?.lua;;'; init_by_lua_file /path/to/config.lua; server { listen 80; server_name foo.com; root /path; index index.html index.htm index.php; location / { try_files $uri $uri/ /index.php$is_args$args; } location ~ \.php$ { set $key ""; set $ttl 600; set $skip 1; rewrite_by_lua_file /path/to/guard.lua; srcache_fetch_skip $skip; srcache_store_skip $skip; srcache_fetch GET /memcached key=$key; srcache_store PUT /memcached key=$key&ttl=$ttl; add_header X-Srcache-Fetch $srcache_fetch_status; add_header X-Srcache-Store $srcache_store_status; try_files $uri =404; include fastcgi.conf; fastcgi_pass 127.0.0.1:9000; } location /memcached { internal; content_by_lua_file /path/to/data/memcached.lua; } }
Nginx启动后,会载入config.lua中的配置信息。请求到达后,缺省情况下,SRCache为关闭状态,在guard.lua中,会对当前请求进行正则匹配,一旦匹配成功,那么就会计算出缓存键,并且把SRCache设置为开启状态,最后由memcached.lua完成读写。
看看「config.lua」文件的内容,它主要用来记录一些全局的配置信息:
config = {} config["memcached"] = { {host = "127.0.0.1", port = "11211"}, {host = "127.0.0.1", port = "11212"}, {host = "127.0.0.1", port = "11213"}, } config["ruleset"] = { {pattern = "/test", fields = {x = "number", y = "string"}}, }
看看「guard.lua」文件的内容,它主要用来计算缓存键,并开启SRCache模块:
local uri = string.match(ngx.var.request_uri, "[^?]+") for _, rule in ipairs(config["ruleset"]) do local pattern = rule["pattern"] local option = rule["option"] if ngx.re.match(uri, pattern, option or "") then local ttl = rule["ttl"] if ttl then ngx.var.ttl = ttl end local args = ngx.req.get_uri_args() local fields = rule["fields"] if fields then for name in pairs(args) do if fields[name] then if fields[name] == "number" then if not tonumber(args[name]) then ngx.exit(OK) end end else args[name] = nil end end end local key = { ngx.var.request_method, " ", ngx.var.scheme, "://", ngx.var.host, uri, } args = ngx.encode_args(args); if args ~= "" then key[#key + 1] = "?" key[#key + 1] = args end key = table.concat(key) key = ngx.md5(key) ngx.var.key = key ngx.var.skip = "0" break end end
看看「memcached.lua」文件的内容,它主要通过 Resty库来读写Memcached:
local memcached = require "resty.memcached" local key = ngx.var.arg_key local index = ngx.crc32_long(key) % #config["memcached"] + 1 local host = config["memcached"][index]["host"] local port = config["memcached"][index]["port"] local memc, err = memcached:new() if not memc then ngx.log(ngx.ERR, err) ngx.exit(ngx.HTTP_SERVICE_UNAVAILABLE) end memc:set_timeout(100) local ok, err = memc:connect(host, port) if not ok then ngx.log(ngx.ERR, err) ngx.exit(ngx.HTTP_SERVICE_UNAVAILABLE) end local method = ngx.req.get_method() if method == "GET" then local res, flags, err = memc:get(key) if err then ngx.log(ngx.ERR, err) ngx.exit(ngx.HTTP_SERVICE_UNAVAILABLE) end ngx.print(res) elseif method == "PUT" then local value = ngx.req.get_body_data() local ttl = ngx.var.arg_ttl local ok, err = memc:set(key, value, ttl) if not ok then ngx.log(ngx.ERR, err) ngx.exit(ngx.HTTP_SERVICE_UNAVAILABLE) end else ngx.exit(ngx.HTTP_NOT_ALLOWED) end memc:set_keepalive(1000, 10)
最后一个问题:如何判断缓存是否生效了?试试下面的命令:
shell> curl -v http://foo.com/test?x=123&y=abc < X-Srcache-Fetch: HIT < X-Srcache-Store: BYPASS
关于激活SRCache前后的性能对比,视环境的不同而不同,大家自行实验吧,不过绝对是数量级的提升,更重要的是这一切对业务层完全透明,别愣着了,快试试吧!