剛接觸 lua-resty-redis 的文檔的時候,你可能會驚訝于上面列出的方法之少。 Redis 有好幾十個命令,而Method一節(jié)列出的方法卻寥寥無幾。 事實上,如果仔細(xì)閱讀了文檔,你會在 Method 一節(jié)的開頭讀到這么一段話:
All of the Redis commands have their own methods with the same name except all in lower case. You can find the complete list of Redis commands here: http://redis.io/commands ... In addition to all those redis command methods, the following methods are also provided:
看來不是 lua-resty-redis 支持的方法少,而是大部分方法都不需要單獨列出來。
其實,lua-resty-redis 并沒有顯式定義這一類跟 Redis 命令同名的方法。
熟悉 Redis 的人對 redis.call
應(yīng)該不會感到陌生,這是 Redis Lua 腳本中調(diào)用 Redis 命令的唯一方法。
無論是什么 Redis 命令,你都可以通過它調(diào)用。
lua-resty-redis 內(nèi)部就有一個類似于這樣的方法,它負(fù)責(zé)把請求參數(shù)發(fā)給 Redis,然后處理來自 Redis 的響應(yīng)。
出于易用性,lua-resty-redis 用 $command(arg1, arg2)
的形式封裝了 call($command, arg1, arg2)
。每次調(diào)用時可以少打四個字符呢。
由于動態(tài)語言支持動態(tài)生成方法,lua-resty-redis 并不用給每個命令補上一個對應(yīng)的方法,它只需要:
-- 僅為示例,不是真正的實現(xiàn)
local cmds = {
'get', 'set', ...
}
for i = 1, #cmds do
local cmd = cmds[i]
_M[cmd] = function(...)
call(cmd, ...)
end
end
現(xiàn)在,要想支持新的 Redis 命令,往 cmds 里加多一個字符串就好了。這就叫良好的拓展性。
當(dāng)然,有些命令,比如 subscribe
,需要額外的特殊處理。
從 OpenResty 1.11.2 版本開始,lua-resty-redis 模塊使用了一個巧妙的技巧,推遲到實際需要時才動態(tài)生成模塊方法。 依靠惰性生成方法,要想支持新的 Redis 命令,大多數(shù)情況下 lua-resty-redis 連一個字符串都不用加。無需拓展,才是真正的“良好的拓展性”。
前面說到,動態(tài)語言支持動態(tài)生成方法,這不僅意味著可以動態(tài)地生成方法,也意味著可以在運行時 按需 生成方法。
跟其他動態(tài)語言一樣,Lua 提供了一個方法,允許程序員在找不到對應(yīng)方法時調(diào)用特定的處理邏輯,那就是 __index
。
__index
在前面的元表一章中已經(jīng)介紹過了。跟“給表中的鍵附上默認(rèn)值”類似,我們也可以給模塊中的方法名附上默認(rèn)實現(xiàn)。
所需的只是如下的代碼:
setmetatable(_M, {__index = function(self, cmd)
local method =
function (self, ...)
return call(self, cmd, ...)
end
-- cache the lazily generated method in our
-- module table
_M[cmd] = method
return method
end})
現(xiàn)在我們可以不用準(zhǔn)備一份超長的命令列表,也無需為用不到的命令付生成方法的開銷,同時給未來的命令也留好了位置。 一切魔法均隱藏于代碼之中,may the source be with you!