local dict = "my_locks" local key1 = "key1" local key2 = "key2" local lock, err = resty_lock:new(dict) ifnot lock then ngx.log(ngx.ERR, "failed to create lock: ", err) return end
local elapsed, err = lock:lock(key1) ifnot elapsed then ngx.log(ngx.ERR, "failed to acquire the lock: ", err) return end
-- 此处调用会出现 err: locked local elapsed, err = lock:lock(key2) ifnot elapsed then ngx.log(ngx.ERR, "failed to acquire the lock: ", err) return end
3. unlock
1
syntax: ok, err = obj:unlock()
释放锁,obj 为 new 返回值。成功返回 1,否则返回 nil,err 标明错误信息。
4. expire
1
syntax: ok, err = obj:expire(timeout)
设置当前 resty.lock 对象实例持有的锁的 TTL,这将把锁实例的超时时间设置为 timeout 参数指定的值(秒)。expire 设置的超时时间与调用 new 指定的超时时间是独立的(不会覆盖 new 指定的超时时间),调用 expire(nil) 会使用 new 函数指定的超时时间。
local resty_lock = require"resty.lock" local cache = ngx.shared.my_cache
-- step 1: local val, err = cache:get(key) if val then ngx.say("result: ", val) return end
if err then return fail("failed to get key from shm: ", err) end
-- cache miss! -- step 2: local lock, err = resty_lock:new("my_locks") ifnot lock then return fail("failed to create lock: ", err) end
local elapsed, err = lock:lock(key) ifnot elapsed then return fail("failed to acquire the lock: ", err) end
-- lock successfully acquired!
-- step 3: -- someone might have already put the value into the cache -- so we check it here again: val, err = cache:get(key) if val then local ok, err = lock:unlock() ifnot ok then return fail("failed to unlock: ", err) end
ngx.say("result: ", val) return end
--- step 4: local val = fetch_redis(key) ifnot val then local ok, err = lock:unlock() ifnot ok then return fail("failed to unlock: ", err) end
-- FIXME: we should handle the backend miss more carefully -- here, like inserting a stub value into the cache.
ngx.say("no value found") return end
-- update the shm cache with the newly fetched value local ok, err = cache:set(key, val, 1) ifnot ok then local ok, err = lock:unlock() ifnot ok then return fail("failed to unlock: ", err) end
return fail("failed to update shm cache: ", err) end
local ok, err = lock:unlock() ifnot ok then return fail("failed to unlock: ", err) end
function_M.new(_, dict_name, opts) local dict = shared[dict_name] ifnot dict then returnnil, "dictionary not found" end -- 创建 cdata 类型数据 -- 主要利用 cdata 的 _gc 元方法(在 ctype 定义中有设置) local cdata = ffi_new(ctype) cdata.key_id = 0 -- 将 cdata 存储在 memo 表中,并返回其索引 cdata.dict_id = ref_obj(dict)
local timeout, exptime, step, ratio, max_step if opts then timeout = opts.timeout exptime = opts.exptime step = opts.step ratio = opts.ratio max_step = opts.max_step end
ifnot exptime then exptime = 30 end
if timeout and timeout > exptime then timeout = exptime end
// 加锁操作 function_M.lock(self, key) ifnot key then returnnil, "nil key" end
local dict = self.dict local cdata = self.cdata -- cdata 已经存在锁 if cdata.key_id > 0then returnnil, "locked" end -- 在 shared dict 添加 key local exptime = self.exptime local ok, err = dict:add(key, true, exptime) if ok then -- 加锁成功,增加 key 的引用 -- 将 key 存储在 memo table 中,避免被 GC 掉 cdata.key_id = ref_obj(key) ifnot shdict_mt then shdict_mt = getmetatable(dict) end return0 end -- 出错,直接返回 if err ~= "exists"then returnnil, err end -- 不断重试,尝试获得锁 -- lock held by others local step = self.step local ratio = self.ratio local timeout = self.timeout local max_step = self.max_step local elapsed = 0 while timeout > 0do if step > timeout then step = timeout end
local ok, err = dict:add(key, true, exptime) if ok then cdata.key_id = ref_obj(key) ifnot shdict_mt then shdict_mt = getmetatable(dict) end return elapsed end
if err ~= "exists"then returnnil, err end
if timeout <= 0then break end
step = step * ratio if step <= 0then step = 0.001 end if step > max_step then step = max_step end end
function_M.unlock(self) local dict = self.dict local cdata = self.cdata local key_id = tonumber(cdata.key_id) if key_id <= 0then returnnil, "unlocked" end