【SpringBoot】Redis Lua腳本實戰指南:簡單高效的構建分布式多命令原子操作、分布式鎖

文章目錄

  • 一.Lua腳本
    • 1.Lua特性
    • 2.Lua優勢
  • 二.Lua語法
    • 1.注釋
    • 2.變量
    • 3.數據類型:
      • 3.1.基本類型
      • 3.2.對象類型:表(table)
    • 4.控制結構:
      • 4.1.條件語句: 使用if、else和elseif來實現條件分支。
      • 4.2.循環結構:Lua支持for循環、while循環和repeat...until循環。
    • 5.函數
      • 5.1.函數的定義與調用
      • 5.2.匿名函數與閉包
    • 6.模塊
      • 1. 創建模塊
      • 2.使用模塊
      • 3.注意事項
    • 6.字符串操作
    • 7.錯誤處理
      • 7.1.pcall
      • 7.2.xpcall
      • 7.3.自定義錯誤
    • 8.創建線程和協程
    • 9.如何使用lua標準庫
  • 三.Lua腳本的應用場景
    • 1. 緩存更新:
    • 2. 原子操作:
    • 3. 數據處理:
    • 4. 分布式鎖:
  • 四.Spring Boot中集成Lua腳本
    • 1. 添加依賴
    • 2.修改配置文件
    • 3.創建Lua腳本
    • 4.編寫Java代碼以加載和執行Lua腳本
      • 4.1.直接運行內嵌在Java代碼中Lua腳本字符串
      • 4.2.加載和運行Lua腳本文件

一.Lua腳本

Lua是一種輕量級、可嵌入的,自帶原子性腳本語言,用標準C語言編寫并以源代碼形式開放, 其設計目的是為了嵌入應用程序中,從而為應用程序提供靈活的擴展和定制功能。廣泛應用于游戲開發、Web開發和其他領域。``

1.Lua特性

  • 輕量級: 它用標準C語言編寫并以源代碼形式開放,編譯后僅僅一百余K,可以很方便的嵌入別的程序里。
  • 可擴展: Lua提供了非常易于使用的擴展接口和機制:由宿主語言(通常是C或C++)提供這些功能,Lua可以使用它們,就像是本來就內置的功能一樣。
  • 數據類型豐富,包括數字、字符串、布爾值、表(數組和字典的集合)等。這些數據類型使得Lua語言能夠處理各種復雜的數據結構和算法。
  • 支持面向過程(procedure-oriented)編程和函數式編程(functional programming);
  • 自動內存管理;只提供了一種通用類型的表(table),用它可以實現數組,哈希表,集合,對象;
  • 語言內置模式匹配;閉包(closure);函數也可以看做一個值;提供多線程(協同進程,并非操作系統所支持的線程)支持;
  • 通過閉包和table可以很方便地支持面向對象編程所需要的一些關鍵機制,比如數據抽象,虛函數,繼承和重載等。

2.Lua優勢

Lua腳本在Redis中的使用有許多優勢,使其成為執行復雜操作的理想選擇。以下是一些主要原因:

性能

  • Lua腳本在Redis中執行,避免了多次的客戶端與服務器之間的通信。這可以減少網絡開銷,提高性能,特別是在需要執行多個Redis命令以完成一個操作時。原子性:Redis保證Lua腳本的原子性執行,無需擔心競態條件或并發問題。

事務

  • Lua腳本可以與Redis事務一起使用,確保一系列命令的原子性執行。這允許你將多個操作視為一個單一的事務,要么全部成功,要么全部失敗

復雜操作

  • Lua腳本提供了一種在Redis中執行復雜操作的方法,允許你在一個腳本中組合多個Redis命令。這對于處理復雜的業務邏輯非常有用,例計算和更新分布式計數器、實現自定義數據結構等。

實現復雜的原子鎖

  • Lua腳本的執行是原子的,這意味著在Lua腳本執行期間,沒有其他客戶端可以插入其他操作。這使得Lua腳本在實現諸如分布式鎖、計數器、排行榜等需要原子操作的情況下非常有用。

  • 使用Lua腳本,你可以實現復雜的原子鎖,而不僅僅是使用Redis的SETNX(set if not exists)命令。這對于分布式鎖的實現非常重要。

減少網絡開銷

  • 對于大批量的數據處理,Lua腳本可以減少客戶端和服務器之間的往返次數,從而顯著減少網絡開銷。

減少服務器負載
通過將復雜的計算移至服務器端,可以減輕客戶端的負擔,降低服務器的負載。

原生支持

  • Redis天生支持Lua腳本,因此不需要額外的插件或擴展。

可讀性和維護性

  • Lua腳本是一種常見的腳本語言,易于編寫和維護。將復雜邏輯封裝在腳本中有助于提高代碼的可讀性。

Lua腳本在Redis中的優勢在于它可以原子性地執行復雜操作、減少網絡通信、提高性能、減輕服務器負載,以及提高代碼的可讀性。這使得它成為執行一系列復雜操作的理想選擇,尤其是在分布式系統中需要高性能和可伸縮性的場景下。通過Lua腳本,Redis不僅成為一個鍵值存儲,還能執行復雜的數據操作。

二.Lua語法

在線運行lua腳本網站https://www.bejson.com/runcode/lua/

1.注釋

注釋在Lua中用于添加說明和注解。單行注釋以–開始,多行注釋則使用–[[ … ]]。

-- 這是一條單行注釋--[[ 這是一個多行注釋可以跨越多行
]]
  • 單行注釋:以2個連續短橫線--開始,直到該行結束
  • 多行注釋:使用2個方括號[[開始,并以2個方括號]]結束

2.變量

變量在Lua中無需顯式聲明類型。使用local關鍵字創建局部變量,全局變量直接聲明。

local age = 30
name = "John" -- 全局變量

3.數據類型:

3.1.基本類型

基本數據類型包括整數、浮點數、字符串、布爾值和nil。

  • 表tabile是一種非用 {}存儲鍵值對類型數據,類似java的對象
-- 聲明不同類型的變量  
local num = 42  -- 整數  
local num2 = 3.14  -- 浮點數
local bool1 = true  -- true  
local bool2 = false  -- false
local str1 = "Hello, World!"  -- 雙引號字符串  
local str2 = 'Lua is great!'  -- 單引號字符串

3.2.對象類型:表(table)

表是Lua的核心數據結構,用花括號{}定義。

  • 表可以包含鍵值對,鍵和值可以是任何數據類型。

    local person = { name = "John", age = 30, hobbies = {"Reading", "Gaming"} }
    print("姓名:" .. person.name)
    print("年齡:" .. person.age)
    

4.控制結構:

4.1.條件語句: 使用if、else和elseif來實現條件分支。

local age = 1
if age < 18 thenprint("未成年人")
elseif age >= 18 and age < 66 thenprint("成年人")
elseprint("老年人")
end
-- 未成年人

4.2.循環結構:Lua支持for循環、while循環和repeat…until循環。

for循環

for i = 1, 5 doprint(i)
end

while循環

local count = 0
while count < 3 doprint("循環次數: " .. count)count = count + 1
end

repeat…until循環

local count = 6
repeatprint("至少執行一次")
until count > 5
-- 至少執行一次

5.函數

函數在Lua中使用function+end關鍵字定義,可以接收參數并返回值

function functionName(arg1, arg2, ...)  -- 函數體  -- 這里是函數要執行的代碼  -- 可以使用參數arg1, arg2, ...  return result -- (可選)返回結果  
end

5.1.函數的定義與調用

	--  定義函數function add(a, b)  local sum = a + b  return sum  end-- 調用add函數并打印結果  local result = add(3, 4)  print("The sum is: " .. result) -- 輸出 "The sum is: 7"

5.2.匿名函數與閉包

-- 定義一個函數,它接受一個函數作為參數并調用它  
function callFunction(func)  func() -- 調用傳遞進來的匿名函數  
end  -- 創建一個匿名函數,并作為參數傳遞給callFunction  
callFunction(function()  print("Hello from an anonymous function!")  
end)  -- 輸出:  
-- Hello from an anonymous function!
local outerVariable = "I'm outside!"  -- 創建一個閉包  
local function createClosure()  return function()  print(outerVariable) -- 訪問外部變量  end  
end  -- 獲取閉包并調用它  
local closure = createClosure()  
closure() -- 輸出:I'm outside!

6.模塊

Lua支持模塊化編程,允許將相關功能封裝在獨立的模塊中,并通過require關鍵字加載它們。

1. 創建模塊

首先,我們創建一個名為myModule.lua的模塊文件。在這個文件中,我們定義了一些函數和變量,并在文件末尾返回一個表,該表包含了模塊提供的所有公開功能和數據。

-- myModule.lua  -- 私有變量  
local privateVar = "This is a private variable"  -- 私有函數  
local function privateFunction()  print("This is a private function")  
end  -- 公開函數  
function myModule.publicFunction()  print("This is a public function")  print("Private variable is: " .. privateVar)  -- 可以訪問私有變量  
end  -- 返回公開部分  
return myModule
  • 注意:在這個示例中,我們使用了myModule這個表來存儲公開的函數。這不是必須的,但這樣做可以使模塊的結構更清晰。

2.使用模塊

在另一個Lua腳本中,我們可以使用require函數來加載并使用myModule模塊。

-- main.lua  -- 加載模塊  
local myModule = require("myModule")  -- 調用模塊中的公開函數  
myModule.publicFunction()  -- 嘗試訪問模塊的私有部分(會失敗)  
-- print(myModule.privateVar)  -- 這將引發錯誤,因為privateVar是私有的  
-- myModule.privateFunction()  -- 這也會引發錯誤,因為privateFunction是私有的

3.注意事項

  • 當使用require加載一個模塊時,Lua會首先檢查模塊是否已經被加載過。如果是,則直接返回之前加載的模塊,而不是重新加載。這有助于避免重復加載和初始化模塊。

  • 模塊的路徑可以是相對路徑或絕對路徑。在上面的示例中,假設myModule.lua和main.lua在同一目錄下,所以只使用了模塊名作為參數。如果模塊在其他位置,你需要提供正確的路徑

  • Lua的模塊系統相對簡單,沒有像一些其他語言那樣復雜的包管理系統。但是,你可以使用像LuaRocks這樣的第三方工具來管理和安裝Lua包

6.字符串操作

Lua提供了豐富的字符串操作功能,包括字符串連接、查找、替換、模式匹配

字符串連接

  • Lua中的字符串連接操作非常簡單,只需將兩個字符串相鄰放置即可。

    local str1 = "Hello"  
    local str2 = "World"  
    local result = str1 .. str2  -- 連接字符串  
    print(result)  -- 輸出:HelloWorld
    

字符串長度

  • 使用#操作符可以獲取字符串的長度。

    local str = "Hello"  
    print(#str)  -- 輸出:5
    

字符串查找

  • 使用string.find函數可以在字符串中查找子串。該函數返回兩個值:子串開始的位置結束的位置(如果不存在則返回nil)。

    local str = "Hello, World!"  
    local start, end_ = string.find(str, "World")  if start then  print("Found 'World' at position", start)  
    else  print("Not found")  
    end
    

字符串替換

  • 使用string.gsub函數可以全局替換字符串中的子串。第一個參數是源字符串第二個參數是要被替換的模式第三個參數是替換成的字符串,第四個參數(可選)是一個計數器,表示替換的最大次數。

    local str = "apple, apple, apple pie"  
    local new_str = string.gsub(str, "apple", "orange")  
    print(new_str)  -- 輸出:orange, orange, orange pie  -- 替換最多兩次  
    local new_str = string.gsub(str, "apple", "orange", 2)  
    print(new_str)  -- 輸出:orange, orange, apple pie
    

字符串切分

  • 雖然Lua標準庫沒有直接提供字符串切分函數,但你可以使用string.find和string.sub組合來實現。或者,你可以使用第三方庫,如lua-string庫中的split函數
    function split(str, delim)  local result = {}  local from = 1  local delim_from, delim_to = string.find(str, delim, from)  while delim_from do  table.insert(result, string.sub(str, from, delim_from - 1))  from = delim_to + 1  delim_from, delim_to = string.find(str, delim, from)  end  table.insert(result, string.sub(str, from))  return result  
    end  local str = "apple,banana,cherry"  
    local fruits = split(str, ",")  
    for _, fruit in ipairs(fruits) do  print(fruit)  
    end
    -- 執行結果:
    -- apple
    -- banana
    -- cherry
    

字符串格式化

  • Lua沒有內置的字符串格式化函數,但你可以使用string.format函數來模擬這個功能。
    local name = "Alice"  
    local age = 30  
    local greeting = string.format("Hello, my name is %s and I'm %d years old.", name, age)  
    print(greeting)  -- 輸出:Hello, my name is Alice and I'm 30 years old.
    

字符串模式匹配

  • Lua提供了強大的模式匹配功能,通過string.match、string.gmatch和string.find等函數可以實現復雜的字符串處理。Lua的模式匹配基于一種類似于Perl的正則表達式語法。

    local str = "apple 123 orange 456"  
    for number in string.gmatch(str, "%d+") do  print(number)  -- 輸出:123 和 456  
    end
    

7.錯誤處理

在Lua中,錯誤處理通常涉及到使用pcall(protected call)或xpcall(extended protected call)函數來捕獲和處理可能出現的錯誤。當在Lua代碼中遇到錯誤時,它會拋出一個錯誤消息并終止當前的執行。通過使用pcall或xpcall,你可以捕獲這些錯誤并繼續執行后續的代碼。

7.1.pcall

pcall函數接收一個函數和一個可選的參數列表,并嘗試以“保護”模式調用該函數。

  • 如果調用成功,pcall返回true以及函數的返回值
  • 如果調用失敗(即拋出了錯誤),pcall返回false以及錯誤消息
function riskyFunction()  error("Something went wrong!")  
end  local status, result = pcall(riskyFunction)  
if not status then  print("An error occurred: " .. result)  
else  print("Function call was successful: " .. result)  
end
-- An error occurred: script.lua:2: Something went wrong!

7.2.xpcall

xpcall與pcall類似,但它允許你提供一個錯誤處理函數,該函數將在發生錯誤時被調用。這使得你可以進行更復雜的錯誤處理。

function myErrorHandler(err)  print("Caught an error: " .. err)  -- 這里可以進行更復雜的錯誤處理  return debug.traceback(err, 2) -- 返回錯誤跟蹤信息  
end  function riskyFunction()  error("Something went wrong!")  
end  local status, traceback = xpcall(riskyFunction, myErrorHandler)  
if not status then  print("An error occurred:")  print(traceback)  
end

執行結果

Caught an error: script.lua:8: Something went wrong!
An error occurred:
script.lua:8: Something went wrong!
stack traceback:[C]: in function 'error'script.lua:8: in function 'riskyFunction'[C]: in function 'xpcall'script.lua:11: in main chunk[C]: in ?

7.3.自定義錯誤

在Lua中,你可以使用error函數來拋出一個自定義的錯誤。error函數接受一個字符串作為錯誤消息,并可以選擇性地提供一個錯誤級別。

function checkNumber(n)  if type(n) ~= "number" then  error("Not a number!", 2) -- 拋出一個錯誤  end  print("The number is: " .. n)  
end  checkNumber("hello") -- 這將拋出一個錯誤

執行結果

/usr/local/lua-5.3.5/lua53: script.lua:8: Not a number!
stack traceback:[C]: in function 'error'script.lua:3: in function 'checkNumber'script.lua:8: in main chunk[C]: in ?

8.創建線程和協程

-- 創建協程
local cdata = coroutine.create(function () print("Hello from coroutine!") end)  -- 定義函數  
local function say_hello(name) print("Hello, " .. name) end  
-- 調用函數,輸出 "Hello, Alice"
say_hello("Alice")  -- 創建線程  
local thread = coroutine.create(function () print("Hello from thread!") end)  
-- 恢復線程,輸出 "Hello from thread!"
coroutine.resume(thread) -- Hello, Alice
-- Hello from thread!

9.如何使用lua標準庫

string庫:

  • 提供了一系列用于處理字符串的函數,如查找、替換、連接、拆分、格式化等。
  • 例如:string.find(), string.gsub(), string.format() 等。

table庫:
提供了一系列用于操作Lua表的函數,如表的插入、刪除、排序、遍歷等。
例如:table.insert(), table.remove(), table.sort(), table.concat() 等。

math庫:

  • 提供了一系列數學函數,如三角函數、指數函數、對數函數、隨機數生成等。
  • 例如:math.sin(), math.exp(), math.log(), math.random() 等。

io庫:

  • 提供了文件操作的相關函數,用于讀寫文件、處理文件路徑等。
  • 例如: io.open(), io.read(), io.write(), io.lines() 等。

os庫:

  • 提供與操作系統相關的函數,如獲取當前時間、執行系統命令、處理環境變量等。
  • 例如:os.time(), os.execute(), os.getenv(), os.setlocale() 等。

debug庫:

  • 提供了一組用于調試的函數,如獲取堆棧跟蹤、設置和獲取斷點、操作局部變量等。
  • 需要注意的是,debug庫中的一些功能在受限的環境中可能不可用或被禁用。

package庫:

  • 提供了Lua模塊加載和搜索路徑管理的功能。
  • 例如,你可以使用package.path和package.cpath來設置模塊的搜索路徑。

coroutine庫:

  • 提供了協程(coroutine)管理的函數,允許你創建、恢復、讓出和銷毀協程。
  • 協程是一種用戶態的輕量級線程,可以在Lua中實現非阻塞的I/O操作或并發編程。

怎么使用

  • 在Lua中,使用標準庫不需要使用關鍵字進行顯式引入(import)或加載(load)。
    • Lua的標準庫是預編譯并內置在Lua解釋器中的,因此在你的Lua腳本中可以直接調用這些庫中的函數,而無需任何額外的步驟

你只需要直接調用標準庫中的函數即可,例如:
在這里插入圖片描述

三.Lua腳本的應用場景

1. 緩存更新:

場景:在緩存中存儲某些數據,但需要定期或基于條件更新這些數據,同時確保在更新期間不會發生并發問題

  • 示例:使用Lua腳本,你可以原子性地檢查數據的新鮮度,如果需要更新,可以在一個原子性操作中重新計算數據并更新緩存

    -- 獲取鍵名,即從傳入腳本的參數列表KEYS中獲取第1個參數
    local key = KEYS[1] -- 獲取緩存鍵
    -- 根據key從緩存中獲取數據
    local value = redis.call('GET', key ) -- 嘗試從緩存獲取數據-- 如果數據不存在(即data為nil)  
    if not value then-- 數據不在緩存中,重新計算并設置--調用一個名為 calculateData 的函數(這個函數在腳本中沒有定義,但應該是一個用于重新計算數據的函數)value = calculateData()-- 將新計算的數據設置到緩存中  redis.call('SET', key , value )
    end
    -- 返回數據(無論是從緩存中獲取的還是新計算的)
    return value 
    

2. 原子操作:

Lua腳本的執行是原子的,這意味著在Lua腳本執行期間,沒有其他客戶端可以插入其他操作。這使得Lua腳本在實現諸如分布式鎖、計數器、排行榜等需要原子操作的情況下非常有用。

場景:需要執行多個Redis命令作為一個原子操作,確保它們在多線程或多進程環境下不會被中斷。

  • 示例:使用Lua腳本,你可以將多個命令組合成一個原子操作,如實現分布式鎖、計數器、排行榜等。

    -- 獲取鍵名,即從傳入腳本的參數列表KEYS中獲取第1個參數
    local key = KEYS[1]
    --  獲取參數值,這是你想要設置的新值 ,即從傳入腳本的參數列表ARGV 中獲取第1個參數
    local value = ARGV[1] 
    --  獲取當前鍵對應的值
    local current = redis.call('GET', key) -- 如果當前值不存在(即current為nil),或者當前值(轉換為數字后)小于給定的參數值(也轉換為數字后)
    if not current or tonumber(current) < tonumber(value) then -- 如果當前值不存在或新值更大,設置新值redis.call('SET', key, value)
    end
    

3. 數據處理:

場景:需要對Redis中的數據進行復雜的處理,如統計、篩選、聚合等。

  • 示例:使用Lua腳本,你可以在Redis中執行復雜的數據處理,而不必將數據傳輸到客戶端進行處理,減少網絡開銷。

    -- 從傳入腳本的參數列表中獲取第1個參數
    local keyPattern = ARGV[1] -- 調用Redis的KEYS命令,根據給定的模式keyPattern獲取所有匹配的鍵。
    -- 需要注意的是,KEYS命令在生產環境中應謹慎使用,因為它可能會阻塞Redis服務器,尤其是在有大量鍵存在時。
    local keys = redis.call('KEYS', keyPattern)
    -- 初始化一個空的Lua表result,用于存儲處理后的數據
    local result = {} 
    --遍歷keys表中的每一個鍵
    for i, key in ipairs(keys) do-- 獲取每個鍵對應的值 local data = redis.call('GET', key)-- 調用一個假設存在的processData函數處理這個值(注意:在腳本中并沒有給出processData函數的定義,你需要根據實際需求來實現它)。-- 然后處理后數據并添加到result表中table.insert(result, processData(data))
    end
    --返回處理后的結果表。這個表包含了所有匹配鍵對應的處理后的數據。
    return result 
    

4. 分布式鎖:

場景:實現分布式系統中的鎖機制,確保只有一個客戶端可以執行關鍵操作。

  • 示例:使用Lua腳本,你可以原子性地嘗試獲取鎖避免競態條件然后在完成后釋放鎖

    -- 獲取鎖的鍵名
    local lockKey = KEYS[1] 
    -- 獲取鎖的值,通常是一個唯一標識符(比如 UUID),用于在釋放鎖時驗證鎖的持有者。
    local lockValue = ARGV[1]
    -- 獲取鎖的超時時間,單位是毫秒,如果在這個時間內鎖沒有被釋放(即 lockKey 沒有被刪除),則 Redis 會自動刪除這個鍵,避免死鎖。
    local lockTimeout = ARGV[2]-- 嘗試使用 SET 命令來設置鎖,NX 表示只有在 key 不存在時設置值,PX 表示 key 的過期時間(毫秒)  
    --  lockKey 不存在(NX),則設置其值為 lockValue,并設置其過期時間為 lockTimeout(PX)。如果設置成功,說明客戶端成功獲取到了鎖。
    -- 如果 SET 命令執行成功,說明獲取到了鎖
    if redis.call('SET', lockKey, lockValue, 'NX', 'PX', lockTimeout) then-- 鎖獲取成功,執行關鍵操作(這部分邏輯在腳本中沒有給出)  -- ...-- 執行完關鍵操作后,刪除鎖鍵,釋放鎖  redis.call('DEL', lockKey) -- 釋放鎖-- 返回 true,表示成功獲取并釋放了鎖  return true
    else-- 鎖獲取失敗,可能是因為有其他客戶端已經持有了該鎖  return false
    

通常用于在分布式系統中確保多個客戶端在并發訪問共享資源時能夠正確地獲取和釋放鎖,從而避免數據不一致的問題。

四.Spring Boot中集成Lua腳本

在Spring Boot中實現Lua腳本的執行主要涉及Spring Data RedisLettuce(或Jedis)客戶端的使用。

1. 添加依賴

添加Spring Data Redis和Lettuce(或Jedis)的依賴。

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency><groupId>io.lettuce.core</groupId><artifactId>lettuce-core</artifactId> 
</dependency>

2.修改配置文件

在application.properties或application.yml中配置Redis連接屬性,包括主機、端口、密碼等。

spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=yourPassword

3.創建Lua腳本

創建一個Lua腳本,以執行你需要的操作。將腳本保存在Spring Boot項目的合適位置。

  1. 假設有一個Lua腳本文件myscript.lua,它實現了一個簡單的計算:

    local a = tonumber(ARGV[1])
    local b = tonumber(ARGV[2])
    return a + b
    

4.編寫Java代碼以加載和執行Lua腳本

  • 使用Spring Data Redis提供的StringRedisTemplateLettuceConnectionFactory

4.1.直接運行內嵌在Java代碼中Lua腳本字符串

  1. 運行Lua腳本字符串:
    @Service
    public class LuaScriptService {@Autowiredprivate StringRedisTemplate stringRedisTemplate;// 運行Lua腳本字符串public Integer executeLuaScriptFromString() {// Lua腳本字符串,該腳本接收兩個參數(ARGV[1] 和 ARGV[2]),將它們轉換為數字并相加  String luaScript = "local a = tonumber(ARGV[1])\nlocal b = tonumber(ARGV[2])\nreturn a + b";// 創建一個Redis腳本對象,指定Lua腳本和期望的返回類型(Integer) RedisScript<Integer> script = new DefaultRedisScript<>(luaScript, Integer.class);// 創建一個空的keys數組,因為在這個Lua腳本中,我們不使用KEYS參數  String[] keys = new String[0]; // 通常情況下,沒有KEYS部分  // 創建一個args數組,包含兩個參數,這些參數將傳遞給Lua腳本  Object[] args = new Object[]{10, 20}; // 傳遞給Lua腳本的參數  // 使用stringRedisTemplate(它應該是已經配置好的Spring Data Redis的StringRedisTemplate實例)  // 執行Lua腳本,并傳入keys和args數組  Integer result = stringRedisTemplate.execute(script, keys, args);  // 返回執行Lua腳本后得到的結果(兩個數字的和)  return result;}
    }
    

4.2.加載和運行Lua腳本文件

  • 將Lua腳本保存到文件,例如myscript.lua。然后創建一個Java類來·加載和運行該腳本文件·:

    @Service
    public class LuaScriptService {@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Autowiredprivate ResourceLoader resourceLoader;// 從文件中執行Lua腳本的方法  public Integer executeLuaScriptFromFile() {// 加載位于類路徑下的myscript.lua資源  Resource resource = resourceLoader.getResource("classpath:myscript.lua");  String luaScript;  try {  // 嘗試讀取資源文件內容,并將其轉換為字符串  luaScript = new String(resource.getInputStream().readAllBytes());  } catch (Exception e) {  // 如果無法讀取Lua腳本文件,則拋出運行時異常  throw new RuntimeException("無法讀取Lua腳本文件。");  }  // 創建一個Redis腳本對象,指定Lua腳本和期望的返回類型(Integer) RedisScript<Integer> script = new DefaultRedisScript<>(luaScript, Integer.class);// 創建一個空的keys數組,因為在這個Lua腳本中,我們不使用KEYS參數  String[] keys = new String[0]; // 通常情況下,沒有KEYS部分  // 創建一個args數組,包含兩個參數,這些參數將傳遞給Lua腳本  Object[] args = new Object[]{10, 20}; // 傳遞給Lua腳本的參數  // 使用stringRedisTemplate(它應該是已經配置好的Spring Data Redis的StringRedisTemplate實例)  // 執行Lua腳本,并傳入keys和args數組  Integer result = stringRedisTemplate.execute(script, keys, args);  // 返回執行Lua腳本后得到的結果(兩個數字的和)  return result;}
    }
    
  1. 運行應用程序

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/10650.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/10650.shtml
英文地址,請注明出處:http://en.pswp.cn/web/10650.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Shell參數擴展形式學習筆記

Shell參數擴展形式學習筆記 文章目錄 Shell參數擴展形式學習筆記空值判斷處理 ${parameter:-word} ${parameter:word} ${parameter:?word} ${parameter:word}變量位置截取 ${parameter:offset} ${parameter:offset:length}變量匹配組合 ${!prefix*} ${!prefix} ${!name[]} ${!…

感知機和神經網絡

引入 什么是神經網絡&#xff1f; 我們今天學習的神經網絡&#xff0c;不是人或動物的神經網絡&#xff0c;但是又是模仿人和動物的神經網絡而定制的神經系統&#xff0c;特別是大腦和神經中樞&#xff0c;定制的系統是一種數學模型或計算機模型&#xff0c;神經網絡由大量的人…

圖像處理:圖像噪聲添加

文章目錄 前言一、高斯噪聲二、椒鹽噪聲三、泊松噪聲四、斑點噪聲五、指數噪聲六、均勻噪聲總結 前言 本文主要介紹幾種添加圖像噪聲的方法&#xff0c;用于數據增強等操作。 以下圖為例。 一、高斯噪聲 高斯噪聲就是給圖片添加一個服從高斯分布的噪聲&#xff0c;可以通過調…

vLLM初探

vLLM是伯克利大學LMSYS組織開源的大語言模型高速推理框架&#xff0c;旨在極大地提升實時場景下的語言模型服務的吞吐與內存使用效率。vLLM是一個快速且易于使用的庫&#xff0c;用于 LLM 推理和服務&#xff0c;可以和HuggingFace 無縫集成。vLLM利用了全新的注意力算法「Page…

Python+PySpark數據計算

1、map算子 對RDD內的元素進行逐個處理&#xff0c;并返回一個新的RDD&#xff0c;可以使用lambda以及鏈式編程&#xff0c;簡化代碼。 注意&#xff1a;再python中的lambda只能有行&#xff0c;如果有多行&#xff0c;要寫成外部函數&#xff1b;&#xff08;T&#xff09;-&…

train_gpt2_fp32.cu - cudaCheck

源碼 // CUDA error checking void cudaCheck(cudaError_t error, const char *file, int line) {if (error ! cudaSuccess) {printf("[CUDA ERROR] at file %s:%d:\n%s\n", file, line,cudaGetErrorString(error));exit(EXIT_FAILURE);} }; 解釋 該函數用于檢查CU…

無人機路徑規劃:基于鯨魚優化算法WOA的復雜城市地形下無人機避障三維航跡規劃,可以修改障礙物及起始點(Matlab代碼)

一、部分代碼 close all clear clc rng(default); %% 載入數據 data.S[50,950,12]; %起點位置 橫坐標與縱坐標需為50的倍數 data.E[950,50,1]; %終點點位置 橫坐標與縱坐標需為50的倍數 data.Obstaclexlsread(data1.xls); data.numObstacleslength(data.Obstacle(:,1)); …

連接和斷開與服務器的連接

要連接到服務器&#xff0c;通常需要在調用mysql時提供一個MySQL用戶名&#xff0c;很可能還需要一個密碼。如果服務器在除了登錄的計算機之外的機器上運行&#xff0c;您還必須指定主機名。聯系您的管理員以找出應該使用哪些連接參數來連接&#xff08;即使用哪個主機、用戶名…

TypeError: can only concatenate str (not “int“) to str

TypeError: can only concatenate str (not "int") to str a 窗前明月光&#xff0c;疑是地上霜。舉頭望明月&#xff0c;低頭思故鄉。 print(str_len len(str_text) : len(a)) 試圖打印出字符串 a 的長度&#xff0c;但是在 Python 中拼接字符串和整數需要使用字符…

【微服務】spring aop實現接口參數變更前后對比和日志記錄

目錄 一、前言 二、spring aop概述 2.1 什么是spring aop 2.2 spring aop特點 2.3 spring aop應用場景 三、spring aop處理通用日志場景 3.1 系統日志類型 3.2 微服務場景下通用日志記錄解決方案 3.2.1 手動記錄 3.2.2 異步隊列es 3.2.3 使用過濾器或攔截器 3.2.4 使…

triton編譯學習

一 流程 Triton-MLIR: 從DSL到PTX - 知乎 (zhihu.com)https://zhuanlan.zhihu.com/p/671434808Superjomns blog | OpenAI/Triton MLIR 遷移工作簡介https://superjom

基于STM32單片機的環境監測系統設計與實現

基于STM32單片機的環境監測系統設計與實現 摘要 隨著環境污染和室內空氣質量問題的日益嚴重&#xff0c;環境監測系統的應用變得尤為重要。本文設計并實現了一種基于STM32單片機的環境監測系統&#xff0c;該系統能夠實時監測并顯示室內環境的溫濕度、甲醛濃度以及二氧化碳濃…

C語言題目:A+B for Input-Output Practice

題目描述 Your task is to calculate the sum of some integers 輸入格式 Input contains an integer N in the first line, and then N lines follow. Each line starts with a integer M, and then M integers follow in the same line 輸出格式 For each group of inpu…

Sass詳解

Sass&#xff08;Syntactically Awesome Stylesheets&#xff09;是一種CSS預處理器&#xff0c;它允許你使用變量、嵌套規則、混入&#xff08;Mixin&#xff09;、繼承等功能來編寫CSS&#xff0c;從而使CSS代碼更加簡潔、易于維護和擴展。下面是Sass的詳細解釋&#xff1a; …

【docker】容器優化:一行命令換源

原理&#xff1a; 根據清華源提供的Ubuntu 軟件倉庫進行sources.list替換 ubuntu | 鏡像站使用幫助 | 清華大學開源軟件鏡像站 | Tsinghua Open Source Mirror 1、換源 echo "">/etc/apt/sources.list \&& echo "# 默認注釋了源碼鏡像以提高 apt …

新iPadPro是怎樣成為蘋果史上最薄產品的|Meta發布AI廣告工具全家桶| “碾碎一切”,蘋果新廣告片引爭議|生成式AI,蘋果傾巢出動

Remini走紅背后&#xff1a;AI生圖會是第一個超級應用嗎&#xff1f;新iPadPro是怎樣成為蘋果史上最薄產品的生成式AI&#xff0c;蘋果傾巢出動Meta發布AI廣告工具全家桶&#xff0c;圖像文本一鍵生成解放打工人蘋果新iPadPro出貨量或達500萬臺&#xff0c;成中尺寸OLED發展關鍵…

8、QT——QLabel使用小記2

前言&#xff1a;記錄開發過程中QLabel的使用&#xff0c;持續更新ing... 開發平臺&#xff1a;Win10 64位 開發環境&#xff1a;Qt Creator 13.0.0 構建環境&#xff1a;Qt 5.15.2 MSVC2019 64位 一、基本屬性 技巧&#xff1a;對于Qlabel這類控件的屬性有一些共同的特點&am…

QToolButton的特殊使用

QToolButton的特殊使用 介紹通過QSS取消點擊時的凹陷效果點擊時的凹陷效果通過QSS取消點擊時的凹陷效果 介紹 該篇文章記錄QToolButton使用過程中的特殊用法。 通過QSS取消點擊時的凹陷效果 點擊時的凹陷效果 通過QSS取消點擊時的凹陷效果 #include <QToolButton> #i…

Dockerfile中的CMD和ENTRYPOINT

Shell格式和Exec格式 在Dockerfile中&#xff0c;RUN、CMD和ENTRYPOINT指令都可以使用兩種格式&#xff1a;Shell格式和Exec格式。 exec 格式&#xff1a;INSTRUCTION ["executable","param1","param2"] shell 格式&#xff1a; INSTRUCTION c…

【深耕 Python】Quantum Computing 量子計算機(5)量子物理概念(二)

寫在前面 往期量子計算機博客&#xff1a; 【深耕 Python】Quantum Computing 量子計算機&#xff08;1&#xff09;圖像繪制基礎 【深耕 Python】Quantum Computing 量子計算機&#xff08;2&#xff09;繪制電子運動平面波 【深耕 Python】Quantum Computing 量子計算機&…