1. 程式人生 > >像除錯java一樣來除錯Redis lua

像除錯java一樣來除錯Redis lua

高併發的系統中,redis的使用是非常頻繁的,而lua指令碼則更是錦上添花。因為lua指令碼本身執行的時候是一個事務性的操作,不會摻雜其他外部的命令,所以很多關鍵的系統節點都會用redis+lua來實現一致性的操作請求。但是在實際開發過程中,由於redis lua指令碼除錯難的問題,導致大量的時間耗費在了這上面。如果有什麼方案能夠讓我們像利用IDEA除錯java一樣簡便去除錯redis lua指令碼,那該是很幸福的事兒了。

通過不斷的尋找,終於也找到了這種方式,下面就總體的來介紹一下。

1. 下載ZeroBraneStudio,解壓到本地環境,然後找到直譯器路徑,比如我本機是D:\soft\pkulchenko-ZeroBraneStudio-7a8027e\bin\lua.exe,那麼我們就可以通過如下的cmd命令,將D:\soft\pkulchenko-ZeroBraneStudio-7a8027e\bin\新增到Path環境變數中即可。cmd命令如下:

set path=%path%;D:\soft\pkulchenko-ZeroBraneStudio-7a8027e\bin\;

 

2. 下載luaRocks,你可以理解為它類似於python的pip包管理工具,可以利用此工具下載相應的lua包。下載地址為:http://luarocks.github.io/luarocks/releases/,注意選擇其中帶有win32字樣的包,不要選擇帶有windows字樣的包,因為win32字樣的包裡面有install.bat。下載完畢後,解壓,執行install.bat安裝即可。

3. 安裝redis及除錯相關的類庫。舉一反三,既然能安裝redis相關的,那麼也能安裝nginx相關的,所以我們也可以利用此方法來搞定nginx lua開發:

luarocks install remdebug

luarocks install prtr-dump

luarocks install redis-lua

4. 開啟ZeroBraneStudio,建立lua指令碼,開始進行除錯吧,具體步驟如下:

首先,在lua指令碼中,加入下面這段程式碼,以便於讓lua指令碼支援除錯:

local redis = require 'redis'
local host = "192.168.155.126"
local port = 6379
client = redis.connect(host, port)
redis.call = function(cmd, ...) 
    return assert(loadstring('return client:'.. string.lower(cmd) ..'(...)'))(...)
end

從上面可以看出,我們先進行了redis的配置操作,然後加了一個redis.call方法,以便於讓指令碼實現除錯操作。

然後,開始書寫我們正常的業務邏輯,整體程式碼如下:

local redis = require 'redis'
local host = "192.168.155.126"
local port = 6379
client = redis.connect(host, port)
redis.call = function(cmd, ...) 
    return assert(loadstring('return client:'.. string.lower(cmd) ..'(...)'))(...)
end
-- key個數
local keysize = tonumber(3);
--防重主鍵,比如orderid
local pk = 'orderid';
--起始分值
local start_score = tonumber(1);
--截止分值
local end_score = tonumber(3);
--輸入值
local input = tonumber(5);
--限制值
local limit = tonumber(100);
--計算方式
local symbol = '>';
--keys列表
local KEYS = {"pin","ip","phone"}
--hashtag
local hash_tag = '{sumhis}'
--獲取主鍵歷史防重資料
local function get_duplicate_check_data(hash_key_appendix,field_key)
    local hash_key = hash_tag.."pk".. hash_key_appendix;
    local field_val = redis.call("HGET", hash_key , field_key);
    if field_val and field_val ~=nil  and field_val ~= '' then
        return "1";
    end
    return "0";
end
--設定主鍵防重資料
local function set_duplicate_check_data(hash_key_appendix,field_key,input)
    local hash_key = hash_tag.."pk".. hash_key_appendix;
    redis.call("HSET",hash_key,field_key,input);
end
--獲取歷史計數資料
local function get_history_count_data(zset_key_appendix,start_score,end_score)
    local key = hash_tag..zset_key_appendix;
    local hdata = redis.call("ZRANGEBYSCORE",key,start_score,end_score);
    local hdatanum = #hdata;
    local totalNum = 0;
    if hdatanum > 0 then
       for i, buy in pairs(hdata) do
            --拆分字串
            local split = "_";
            local valueSplit = {};
            string.gsub(buy,'[^'..split..']+',function ( w )
                table.insert(valueSplit,w)
            end)
            local orderSkuNum = valueSplit[2];
            totalNum = totalNum + orderSkuNum;
            print("當前"..key.."購買數量為:"..orderSkuNum);
        end
        print("---->當前"..key.."購買總數為:"..totalNum);
    end
    return totalNum;
end
--設定歷史計數資料
local function set_history_count_data(zset_key_appendix,input,end_score,val_prefix)
    local key = hash_tag..zset_key_appendix;
    local value = val_prefix.."_"..input;
    redis.call('ZADD',key,end_score,value);
end
--根據運算子進行數學運算
local function calculate_by_symbol(left, right, symbol)
  if symbol == '+' then
    return left + right
  elseif symbol == '-' then
    return left - right
  elseif symbol == '*' then
    return left * right
  elseif symbol == '/' then
    return left / right
  elseif symbol == '>' then
    return left > right;
  elseif symbol == '<' then
    return left < right;
  elseif symbol == '>=' then
    return left >= right;
  elseif symbol == '<=' then
    return left <= right;
  elseif symbol == '==' then
    return left == right
  elseif symbol == '!=' then
    return left ~= right
  end
end
------主邏輯流程開始------
-- 迴圈處理key 防重校驗,歷史計數資料比對
for i=0, keysize - 1, 1 do
    local key = KEYS[i+1];
    --防重主鍵校驗
    local checkpk = get_duplicate_check_data(key,pk);
    --無防重資訊開始處理
    if checkpk == "0" then
        --redis歷史資料查詢
        local hdata = get_history_count_data(key,start_score,end_score);
        --資料比對
        local calc_rst = calculate_by_symbol(input+hdata, limit, symbol);
        if calc_rst == true then
            print("---->已超限,無法繼續進行購買");
            return "-1";
        else
            print("---->未超限,可以繼續正常購買")
        end
    end    
end
print("---->未超限,重置防重資訊和歷史計數資訊");
--如果無防重資訊且資料未超限
for i=0, keysize - 1, 1 do
    local key = KEYS[i+1];
    set_duplicate_check_data(key,pk,input);
    set_history_count_data(key,input,end_score,pk);
end
return "1";
------主邏輯流程結束------

在書寫程式碼的過程中,我們可以利用print方法來列印日誌,看看日誌部分是不是我們需要的值或者結果。

最後,我們執行程式,開啟除錯模式,先點選想要除錯的程式碼行,下斷點:

 

 

 

 

然後點選下圖圖示的按鈕,開始進行除錯:

 

 

 

 

然後程式就會啟動,開始除錯,我們點選下圖圖示的按鈕,就可以逐語句或者逐過程的進行了:

 

 

 

 

之後我們點選按鈕幾次,就可以走到我們的方法裡面了,同時滑鼠懸停到變數上面,就可以清楚的看到當前變數的值:

 

 

 

 

同時我們也可以在底部的視窗中新增監視變數來監視變數的內容:

 

 

 

 

在控制檯,我們也可以實時看到通過print打印出來的日誌:

 

 

 

 

是不是感覺和IDEA開發java一樣呢?

通過以上的方式,我們就可以非常方便的書寫redis lua,同時進行除錯了。

切記,當redis lua書寫完畢,需要將如下的程式碼段摘掉,然後此lua指令碼就可以載入到redis伺服器中了:

local redis = require 'redis'
local host = "192.168.155.126"
local port = 6379
client = redis.connect(host, port)
redis.call = function(cmd, ...) 
    return assert(loadstring('return client:'.. string.lower(cmd) ..'(...)'))(...)
end