從版本3.2開始,Redis包含一個完整的Lua調試器,可以用來使編寫復雜Redis腳本的任務更加簡單。
由於Redis 3.2仍處於測試階段,請unstable從Github 下載Redis 的分支並編譯它以測試調試器。您可以使用Redis unstable來調試稍后在穩定版本的Redis中運行的腳本,因此調試器已經可用於實際術語。
Redis Lua調試器,代號LDB,具有以下重要功能:
- 它使用服務器 - 客戶端模型,因此它是一個遠程調試器。Redis服務器充當調試服務器,而默認客戶端則是
redis-cli。但是,可以通過遵循服務器實現的簡單協議來開發其他客戶端。 - 默認情況下,每個新的調試會話都是分叉會話。這意味着在調試Redis Lua腳本時,服務器不會阻塞並可用於開發或者為了並行執行多個調試會話。這也意味着在腳本調試會話結束后回滾更改,因此可以使用與上一個調試會話完全相同的Redis數據集再次重新啟動新的調試會話。
- 可以根據需要使用備用同步(非分叉)調試模型,以便可以保留對數據集的更改。在此模式下,服務器會阻止調試會話處於活動狀態。
- 支持逐步執行。
- 支持靜態和動態斷點。
- 支持將調試腳本記錄到調試器控制台中。
- 檢查Lua變量。
- 跟蹤由腳本執行的Redis命令。
- Redis和Lua值的漂亮印刷。
- 無限循環和長執行檢測,模擬斷點。
快速開始
開始使用Lua調試器的一個簡單方法是觀看此視頻介紹:
重要說明:請確保使用Redis生產服務器避免調試Lua腳本。請改用開發服務器。另請注意,使用同步調試模式(這不是默認設置)會導致Redis服務器阻塞調試會話持續的所有時間。
要使用redis-cli以下步驟啟動新的調試會話:
- 使用首選編輯器在某個文件中創建腳本。假設您正在編輯位於的Redis Lua腳本
/tmp/script.lua。 -
啟動調試會話:
./redis-cli --ldb --eval /tmp/script.lua
請注意,通過--eval選項,redis-cli您可以將鍵名和參數傳遞給腳本,並以逗號分隔,如下例所示:
./redis-cli --ldb --eval /tmp/script.lua mykey somekey , arg1 arg2
您將進入一個redis-cli不再接受其正常命令的特殊模式,而是打印一個幫助屏幕並將未修改的調試命令直接傳遞給Redis。
唯一沒有傳遞給Redis調試器的命令是:
quit- 這將終止調試會話。這就像刪除所有斷點並使用continuedebugging命令一樣。此外,命令將退出redis-cli。restart- 調試會話將從頭開始重新啟動,從文件重新加載新版本的腳本。因此,正常的調試周期包括在調試之后修改腳本,並調用restart以便在新腳本更改時再次開始調試。help- 此命令傳遞給Redis Lua調試器,該調試器將打印如下命令列表:
lua debugger> help
Redis Lua debugger help:
[h]elp Show this help.
[s]tep Run current line and stop again.
[n]ext Alias for step.
[c]continue Run till next breakpoint.
[l]list List source code around current line.
[l]list [line] List source code around [line].
line = 0 means: current position.
[l]list [line] [ctx] In this form [ctx] specifies how many lines
to show before/after [line].
[w]hole List all source code. Alias for 'list 1 1000000'.
[p]rint Show all the local variables.
[p]rint <var> Show the value of the specified variable.
Can also show global vars KEYS and ARGV.
[b]reak Show all breakpoints.
[b]reak <line> Add a breakpoint to the specified line.
[b]reak -<line> Remove breakpoint from the specified line.
[b]reak 0 Remove all breakpoints.
[t]race Show a backtrace.
[e]eval <code> Execute some Lua code (in a different callframe).
[r]edis <cmd> Execute a Redis command.
[m]axlen [len] Trim logged Redis replies and Lua var dumps to len.
Specifying zero as <len> means unlimited.
[a]abort Stop the execution of the script. In sync
mode dataset changes will be retained.
Debugger functions you can call from Lua scripts:
redis.debug() Produce logs in the debugger console.
redis.breakpoint() Stop execution as if there was a breakpoint in the
next line of code.
請注意,啟動調試器時,它將以步進模式啟動。它將停止在腳本的第一行,它在執行之前實際執行某些操作。
從這一點開始,您通常會調用step以執行該行並轉到下一行。在您執行步驟時,Redis將顯示服務器執行的所有命令,如以下示例所示:
* Stopped at 1, stop reason = step over
-> 1 redis.call('ping')
lua debugger> step
<redis> ping
<reply> "+PONG"
* Stopped at 2, stop reason = step over
的<redis>和<reply>線示出通過僅執行的行執行的命令,以及來自服務器的答復。請注意,這僅在步進模式下發生。如果您使用continue以執行腳本直到下一個斷點,則不會將命令轉儲到屏幕上以防止輸出過多。
終止調試會話
當腳本自然終止時,調試會話結束並 redis-cli以正常的非調試模式返回。您可以像往常一樣使用該restart命令重新啟動會話。
停止調試會話的另一種方法是redis-cli 通過按下手動中斷Ctrl+C。請注意,也無論如何打破之間的連接redis-cli和redis-server將中斷調試會話。
關閉服務器時,所有分叉的調試會話都將終止。
縮寫調試命令
調試可能是一個非常重復的任務。因此,每個Redis調試器命令都以不同的字符開頭,您可以使用單個初始字符來引用該命令。
因此,例如,step您可以輸入而不是鍵入s。
斷點
如在線幫助中所述,添加和刪除斷點很簡單。只需使用b 1 2 3 4在第1,2,3,4行添加b 0斷點。該命令將刪除所有斷點。可以使用as參數刪除選定的斷點,即我們要刪除的斷點所在的行,但前綴為減號。例如b -3,從第3行中刪除斷點。
請注意,將斷點添加到Lua從不執行的行(如聲明局部變量或注釋)將不起作用。斷點將被添加,但由於腳本的這部分將永遠不會被執行,程序將永遠不會停止。
動態斷點
使用該breakpoint命令可以將斷點添加到特定行中。但是,有時我們只想在發生特殊情況時才停止執行程序。為此,您可以使用redis.breakpoint()Lua腳本中的 函數。調用時,它會模擬下一行中將要執行的斷點。
if counter > 10 then redis.breakpoint() end
此功能在調試時非常有用,因此我們可以避免多次手動繼續執行腳本,直到遇到給定條件。
同步模式
如前所述,但默認LDB使用分叉會話來回滾腳本在調試時所操作的所有數據更改。在調試期間,確定性通常是一件好事,因此可以啟動連續的調試會話,而無需將數據庫內容重置為其原始狀態。
但是,為了跟蹤某些錯誤,您可能希望保留每個調試會話對密鑰空間執行的更改。當這是一個好主意,你應該使用一個特殊的選項,啟動調試器ldb-sync-mode中redis-cli。
./redis-cli --ldb-sync-mode --eval /tmp/script.lua
請注意,在此模式下,在調試會話期間無法訪問Redis服務器,因此請小心使用。
在此特殊模式下,該abort命令可以使操作中的更改中途停止腳本到數據集。請注意,與正常結束調試會話相比,這是不同的。如果你只是中斷redis-cli腳本將完全執行,然后會話終止。相反,abort您可以在中間中斷腳本執行,並在需要時啟動新的調試會話。
從腳本記錄
該redis.debug()命令是一個功能強大的調試工具,可以在Redis Lua腳本中調用,以便將內容記錄到調試控制台中:
lua debugger> list
-> 1 local a = {1,2,3}
2 local b = false
3 redis.debug(a,b)
lua debugger> continue
<debug> line 3: {1; 2; 3}, false
如果腳本在調試會話之外執行,redis.debug()則根本不起作用。請注意,該函數接受多個參數,這些參數由輸出中的逗號和空格分隔。
正確顯示表和嵌套表,以便為程序員調試腳本使值易於觀察。
使用print和檢查程序狀態eval
雖然redis.debug()可以使用該函數直接從Lua腳本中打印值,但通常在步進或停止到斷點時觀察程序的局部變量很有用。
該print命令就是這樣,並在調用幀中執行查找,從當前的一個回到之前的一個,直到頂層。這意味着即使我們進入Lua腳本中的嵌套函數,我們仍然可以使用print foo查看foo調用函數的上下文中的值。在沒有變量名稱的情況下調用時,print將打印所有變量及其各自的值。
該eval命令在當前調用幀的上下文之外執行小塊Lua腳本(使用當前Lua內部結構無法在當前調用幀的上下文中進行評估)。但是,您可以使用此命令來測試Lua函數。
lua debugger> e redis.sha1hex('foo')
<retval> "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
調試客戶端
LDB使用客戶端 - 服務器模型,其中Redis服務器充當使用RESP進行通信的調試服務器。雖然redis-cli是默認的調試客戶端,但只要滿足以下條件之一,任何客戶端都可用於調試:
- 客戶端提供本機接口,用於設置調試模式和控制調試會話。
- 客戶端提供了一個通過RESP發送任意命令的接口。
- 客戶端允許將原始消息發送到Redis服務器。
例如,Redis的插件用於ZeroBrane工作室使用LDB集成Redis的-盧阿。以下Lua代碼是插件如何實現的簡化示例:
local redis = require 'redis'
-- add LDB's Continue command
redis.commands['ldbcontinue'] = redis.command('C')
-- script to be debugged
local script = [[
local x, y = tonumber(ARGV[1]), tonumber(ARGV[2])
local result = x * y
return result
]]
local client = redis.connect('127.0.0.1', 6379)
client:script("DEBUG", "YES")
print(unpack(client:eval(script, 0, 6, 9)))
client:ldbcontinue()

