lua編寫游戲腳本教程,lua游戲腳本實例源碼_Redis Lua腳本中學教程(上)

 2023-11-18 阅读 21 评论 0

摘要:失蹤人口回來啦!有讀者問我為什么這么久都沒有出Redis Lua中學教程,表示村頭廁所已經好久沒有紙了。其實我早就要寫這篇中學教程了,奈何最近太忙了,就一拖再拖,直到今天我終于又開始動筆了。忘記Lua相關概念的同學可以先回顧一下小學教程。

82f6acfb1107d8c22fc1d9027979b545.png

失蹤人口回來啦!

有讀者問我為什么這么久都沒有出Redis Lua中學教程,表示村頭廁所已經好久沒有紙了。其實我早就要寫這篇中學教程了,奈何最近太忙了,就一拖再拖,直到今天我終于又開始動筆了。忘記Lua相關概念的同學可以先回顧一下小學教程。

lua編寫游戲腳本教程。中學教程主要分為兩部分:Redis Lua的相關命令詳解和Lua的語法介紹。

前面我們簡單介紹了EVAL和EVALSHA命令。但是只有那點只是是沒辦法從中學畢業的,因此我們需要進行更深入的學習。

EVAL

最早可用版本:2.6.0

用法:EVAL script numkeys key [key ...] arg [arg ...]

lua腳本怎么寫?關于用法我們已經演示過了,其中第一個參數是要執行的Lua腳本,第二個參數是傳入腳本的參數個數。后面則是參數的key數組和value數組。

在Lua中執行Redis命令的方法我們也介紹過,就是使用redis.call()和redis.pcall()兩個函數。它們之間唯一的不同就是當Redis命令執行錯誤時,redis.call()會拋出這個錯誤,使EVAL命令拋出錯誤,而redis.pcall()會捕獲這個錯誤,并返回Lua的錯誤表。

通常我們約定執行命令的key都需要由參數傳入,命令必須在執行之前進行分析,以確定它作用于哪個key。這樣做的目的是為了在一定程度上保證EVAL執行的Lua腳本的正確性。

Lua和Redis之間數據類型的轉換

在Redis執行EVAL命令時,如果腳本中有call()或者pcall()命令,就會涉及到Redis和Lua之間數據類型轉換的問題。轉換規則要求,一個Redis的返回值轉換成Lua數據類型后,再轉換成Redis數據類型,其結果必須和初始值相同。所以每種類型是一一對應的。轉換規則如下:

Redis與Lua互相轉換

andlua輔助源碼?

3bf199b53018d243e060aea3d503a228.png

除此之外,Lua到Redis的轉換還有一些其他的規則:

  • Lua boolean true -> Redis integer reply with value of 1
  • Lua只有一種數字類型,不會區分整數和浮點數。而數字類型只能轉換成Redis的integer類型,如果要返回浮點數,那么在Lua中就需要返回一個字符串。
  • Lua數組在轉換成Redis類型時,遇到nil就停止轉換

來個栗子驗證一下:

EVAL "return {1,2,3.3333,'foo',nil,'bar'}" 0
1) (integer) 1
2) (integer) 2
3) (integer) 3
4) "foo"

可以看到bar沒有返回,并且3.333返回了3。

腳本的原子性

lua編程指南。Redis運行所有的Lua命令都使用相同的Lua解釋器。當一個腳本正在執行時,其他的腳本或Redis命令都不能執行。這很像Redis的事務multi/exec。這意味著我們要盡量避免腳本的執行時間過長。

腳本整體復制

當腳本進行傳播或者寫入AOF文件時,Redis通常會將腳本本身進行傳播或寫入AOF,而不是使用它產生的若干命令。原因很簡單,傳播整個腳本要比傳播一大堆生成的命令的速度要快。

從Redis3.2開始,可以只復制影響腳本執行結果的語句,而不用復制整個腳本。這個復制整個腳本的方法有以下屬性:

  • 如果輸入相同,腳本必須輸出相同的結果。即執行結果不能依賴于隱式的變量,或依賴于I/O輸入
  • Lua不會導出訪問系統時間或其他外部狀態的命令
  • 如果先執行了“隨機命令”(如RANDOMKEY,SRANDMEMBER,TIME),并改變了數據集,接著執行腳本時會被阻塞。
  • 在Redis4中,Lua腳本調用返回隨機順序的元素的命令時,會在返回之前進行排序,也就是說,調用redis.call("smembers",KEYS[1]),每次返回的順序都相同。從Redis5開始就不需要排序了,因為Redis5復制的是產生影響的命令。
  • Lua修改了偽隨機函數math.random和math.randomseed,使每次執行腳本時seed都相同,而如果不執行math.randomseed,只執行math.random時,每次的結果也都相同。

復制命令隊列

在這種模式下,Redis在執行腳本時會收集所有影響數據集的命令,當腳本執行完畢時,命令隊列會被放在事務中,發送給AOF文件。

python lua,Lua可以通過執行redis.replicate_commands()函數來檢查復制模式,如果返回true表示當前是復制命令模式,如果返回false,則是復制整個腳本模式。

可選擇的復制命令

腳本復制模式選擇好以后,就可以對復制到副本和AOF的方式進行更多的控制。這是一種高級特性,因為濫用會切斷主從備份,和AOF持久化。如果我們只需要在master上執行某些命令時,這一特性就變得很有用。例如我們需要計算一些中間值時,只需要在master上計算就好,那么這些命令就不必進行復制。

從Redis3.2開始,有一個新的命令叫做redis.set_repl(),它可以用來控制復制方式,有如下選項(默認是REPL_ALL):

redis.set_repl(redis.REPL_ALL) -- Replicate to AOF and replicas.
redis.set_repl(redis.REPL_AOF) -- Replicate only to AOF.
redis.set_repl(redis.REPL_REPLICA) -- Replicate only to replicas (Redis >= 5)
redis.set_repl(redis.REPL_SLAVE) -- Used for backward compatibility, the same as REPL_REPLICA.
redis.set_repl(redis.REPL_NONE) -- Don't replicate at all.

全局變量

為了避免數據泄露,Redis腳本不允許創建全局變量。如果必須有一個公共變量,可以使用Redis的key來代替。在EVAL命令中創建一個全局變量會引起一個異常。

> eval 'a=10' 0
(error) ERR Error running script (call to f_933044db579a2f8fd45d8065f04a8d0249383e57): user_script:1: Script attempted to create global variable 'a

關于SELECT的使用

lua游戲源碼?在Lua腳本中使用SELECT就像在正常客戶端中使用一樣。值得一提的是,在Redis2.8.12之前,Lua腳本中執行SELECT是會影響到客戶端的,而從2.8.12開始,Lua腳本中的SELECT只會在腳本執行過程中生效。這點在Redis版本升級時需要注意,因為升級前后,命令的語義會改變。

可用的庫

Lua腳本中有許多庫,但并不是都能在Redis中使用,其中可以使用的有:

  • base lib.
  • table lib.
  • string lib.
  • math lib.
  • struct lib.
  • cjson lib.
  • cmsgpack lib.
  • bitop lib.
  • redis.sha1hex function.
  • redis.breakpoint and redis.debug function in the context of the Redis Lua debugger.

struct, CJSON and cmsgpack是外部庫,其他的都是Lua的標準庫。

在腳本中打印Redis日志

使用redis.log(loglevel,message)函數可以在Lua腳本中打印Redis日志。

Lua腳本?loglevel包括:

  • redis.LOG_DEBUG
  • redis.LOG_VERBOSE
  • redis.LOG_NOTICE
  • redis.LOG_WARNING

它們與Redis的日志等級是對應的。

沙箱和最大執行時間

腳本不應該訪問外部系統,包括文件系統和其他系統。腳本應該只能操作Redis數據和傳入進來的參數。

腳本默認的最大執行時間是5秒(正常腳本執行時間都是毫秒級,所以5秒已經足夠長了)。可以通過修改lua-time-limit變量來控制最大執行時間。

源碼怎么用?當腳本執行時間超過最大執行時間時,并不會被自動終止,因為這違反了腳本的原子性原則。當一個腳本執行時間過長時,Redis會有如下操作:

  • Redis記錄下這個腳本執行時間過長
  • 其他客戶端開始接收命令,但是所有的命令都會會返回繁忙,除了SCRIPT KILL 和 SHUTDOWN NOSAVE
  • 如果一個腳本僅執行只讀命令,則可以用SCRIPT KILL命令來停止它。
  • 如果腳本執行了寫入命令,那么只能用SHUTDOWN NOSAVE來終止服務器,當前的所有數據都不會保存到磁盤。

EVALSHA

最早可用版本:2.6.0

用法:EVALSHA sha1 numkeys key [key ...] arg [arg ...]

該命令用來執行緩存在服務器上的腳本,sha1為腳本的唯一標識。

lua,使用EVAL命令必須每次都要把腳本從客戶端傳到服務器,由于Redis的內部緩存機制,它并不會每次都重新編譯腳本,但是傳輸上仍然浪費帶寬。

另一方面,如果使用特殊命令或者通過redis.conf來定義命令會有以下問題:

  • 不同實例有不同的實現方式
  • 發布將會很困難,特別是分布式環境,因為要保證所有實例都包含給定的命令
  • 讀應用程序代碼時,由于它調用了服務端命令,會不清楚代碼的語義

為了避免這些問題,同時避免浪費帶寬,Redis實現了EVALSHA命令。

如果服務器中沒有緩存指定的腳本,會返回給客戶端腳本不存在的錯誤信息。

SCRIPT DEBUG

lua游戲腳本教程,最早可用版本:3.2.0

時間復雜度:O(1)

用法:SCRIPT DEBUG YES|SYNC|NO

該命令用于設置隨后執行的EVAL命令的調試模式。Redis包含一個完整的Lua調試器,代號為LDB,可以使編寫復雜腳本的任務更加簡單,在調試模式下,Redis充當遠程調試服務器,客戶端可以逐步執行腳本,設置斷點,檢查變量等。想了解更多調試器內容的可以查看官方文檔Redis Lua debugger。

lua腳本編寫教程詳細?LDB可以設置成異步或同步模式。異步模式下,服務器會fork出一個調試會話,不會阻塞主會話,,調試會話結束后,所有數據都會回滾。同步模式則會阻塞會話,并保留調試過程中數據的改變。

SCRIPT EXISTS

最早可用版本:2.6.0

時間復雜度:O(N),N是腳本數量

返回腳本是否存在于緩存中(存在返回1,不存在返回0)。這個命令適合在管道前執行,以保證管道中的所有腳本都已經加載到服務器端了,如果沒有,需要用SCRIPT LOAD命令進行加載。

SCRIPT FLUSH

lua中文手冊、最早可用版本:2.6.0

時間復雜度:O(N),N是緩存中的腳本數

刷新緩存中的腳本,這一命令常在云服務上被使用。

SCRIPT KILL

最早可用版本:2.6.0

時間復雜度:O(1)

停止當前正在執行的Lua腳本,通常用來停止執行時間過長的腳本。停止后,被阻塞的客戶端會拋出一個錯誤。

SCRIPT LOAD

最早可用版本:2.6.0

時間復雜度:O(N),N是腳本的字節數

該命令用于將腳本加載到服務器端的緩存中,但不會執行。加載后,服務器會一直緩存,因為良好的應用程序不太可能有太多不同的腳本導致內存不足。每個腳本都像一個新命令的緩存,所以即使是大型應用程序,也就有幾百個,它們占用的內存是微不足道的。

小結

本文介紹了Redis Lua相關的命令。其中EVAL和EVALSHA用來執行腳本。腳本執行具有原子性。腳本的復制和傳播可以根據需要設置。腳本中不能定義全局變量。

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/4/178777.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息