Last active
April 25, 2022 06:02
-
-
Save lloydzhou/d1dfc41f56866c4b82a6 to your computer and use it in GitHub Desktop.
nginx srcache module to server stale data, using lua-resty-lock to make one request to create new cache, and using "lua-resty-http" + "ngx.timer.at" to update new cache in background.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
upstream www { | |
server 127.0.0.1:9999; | |
} | |
upstream redis { | |
server 127.0.0.1:6379; | |
keepalive 1024; | |
} | |
lua_shared_dict srcache_locks 100k; | |
server { | |
listen 80; | |
server_name cache.example.com; | |
location / { | |
default_type application/json; | |
set $key $uri; | |
set_escape_uri $escaped_key $key; | |
set $staletime 10; | |
set $exptime 15; | |
rewrite_by_lua ' | |
-- if has "X-Skip-Fetch" header will skip srcache_fetch, and create new cache | |
if ngx.var.http_x_skip_fetch then return end | |
local http, parser, lock = require "resty.http", require "redis.parser", require "resty.lock" | |
local key, method, stale = ngx.var.key, ngx.var.request_method, tonumber(ngx.var.stale) or 10 | |
local res = ngx.location.capture("/redisttl", { args={key=key}}) | |
local ttl = parser.parse_reply(res.body) | |
-- stale time, need update the cache | |
if ttl < stale then | |
-- cache missing, no need to using srcache_fetch, go to backend server, and store new cache | |
-- if redis server version is 2.8- can not return -2 !!!!!! | |
if ttl > -2 then | |
-- option: remove expire time for key | |
-- if ttl > 0 then | |
-- ngx.location.capture("/redispersist", { args={ key=key } }) | |
-- end | |
-- get a lock, if success, do not fetch cache, create new one, the lock will release in "exptime". | |
-- if can not get the lock, just using the stale data from redis. | |
local l = lock:new("srcache_locks", {exptime=5, timeout=0.01}) | |
if l and l:lock(key) then | |
if method == "POST" or method == "PUT" then ngx.req.read_body() end | |
local func = function(p, http, uri, method, headers, body) | |
headers["X-Skip-Fetch"] = "TRUE" -- set header force to create new cache. | |
-- pass the "Host" header, and so on | |
http:new():request({ url="http://127.0.0.1" .. uri, method=method, headers=headers, body=body}) | |
ngx.log(ngx.ERR, "[LUA], success to update new cache, uri: ", uri, ", method: ", method) | |
end | |
-- run a backend task, to update new cache | |
ngx.timer.at(0, func, http, ngx.var.request_uri, method, ngx.req.get_headers(), ngx.req.get_body_data()) | |
end | |
end | |
end | |
'; | |
if ($http_x_skip_fetch != TRUE){ srcache_fetch GET /redis $key;} | |
srcache_store PUT /redis2 key=$escaped_key&exptime=$exptime; | |
add_header X-Cache $srcache_fetch_status; | |
add_header X-Store $srcache_store_status; | |
proxy_pass http://www; | |
} | |
location = /redisttl { | |
internal; | |
set_unescape_uri $key $arg_key; | |
set_md5 $key; | |
redis2_query ttl $key; | |
redis2_pass redis; | |
} | |
location = /redispersist { | |
internal; | |
set_unescape_uri $key $arg_key; | |
set_md5 $key; | |
redis2_query persist $key; | |
redis2_pass redis; | |
} | |
location = /redis { | |
internal; | |
set_md5 $redis_key $args; | |
redis_pass redis; | |
} | |
location = /redis2 { | |
internal; | |
set_unescape_uri $exptime $arg_exptime; | |
set_unescape_uri $key $arg_key; | |
set_md5 $key; | |
redis2_query set $key $echo_request_body; | |
redis2_query expire $key $exptime; | |
redis2_pass redis; | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@lloydzhou There is a typo here:
https://gist.github.com/lloydzhou/d1dfc41f56866c4b82a6#file-nginx-conf-L26
You are referring to
ngx.var.stale
but createdstaletime