script.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. package rds
  2. import (
  3. "github.com/go-redis/redis"
  4. "io/ioutil"
  5. "os"
  6. "path/filepath"
  7. "strings"
  8. )
  9. /**
  10. 提供对 redis 中 lua 脚本的简单封装
  11. lua 主要用于逻辑层面的批量 key 操作。 这和 multi 命令不同:
  12. 1. lua 脚本是 atomic 的, 可以看作事务。(和DBMS事务不同,主要不支持回滚,这里仅仅是提供了“一次性”的逻辑)
  13. 2. lua 脚本执行不会中途打断。
  14. 3. lua 脚本相比 multi 有更小的数据传数量
  15. 实现目标
  16. 支持 lua 从 lua 文件加载
  17. 支持从字符串加载
  18. 支持从目录加载
  19. 加载后,需要保存 redis 保存脚本后返回的对应的 hash
  20. 調用方可根据文件名调用
  21. 从字符串加载的,可自己设置名字
  22. 名字不能重名
  23. */
  24. // LuaBlock
  25. type LuaBlock struct {
  26. *redis.Script
  27. name string
  28. }
  29. type luablocker interface {
  30. Eval(script string, keys []string, args ...interface{}) *redis.Cmd
  31. EvalSha(sha1 string, keys []string, args ...interface{}) *redis.Cmd
  32. ScriptExists(hashes ...string) *redis.BoolSliceCmd
  33. ScriptLoad(script string) *redis.StringCmd
  34. }
  35. var _ luablocker = (*redis.Client)(nil)
  36. var _ luablocker = (*redis.Ring)(nil)
  37. var _ luablocker = (*redis.ClusterClient)(nil)
  38. func newLuaBlock(name, block string) *LuaBlock {
  39. return &LuaBlock{
  40. Script: redis.NewScript(block),
  41. name: name,
  42. }
  43. }
  44. // LuaBlockManager
  45. type LuaScripts struct {
  46. m map[string]*LuaBlock
  47. h luablocker
  48. }
  49. func NewLuaScripts(handle luablocker) *LuaScripts {
  50. return &LuaScripts{
  51. m: make(map[string]*LuaBlock),
  52. h: handle,
  53. }
  54. }
  55. func (self *LuaScripts) Exec(name string, keys []string, args ...interface{}) *redis.Cmd {
  56. luablock, ok := self.m[name]
  57. if !ok {
  58. panic("not load lua script:" + name)
  59. }
  60. return luablock.EvalSha(self.h, keys, args)
  61. }
  62. func (self *LuaScripts) LoadString(name string, block string) error {
  63. _, err := self.h.ScriptLoad(block).Result()
  64. if err != nil {
  65. return err
  66. }
  67. self.m[name] = newLuaBlock(name, block)
  68. return nil
  69. }
  70. func (self *LuaScripts) LoadFile(f string) error {
  71. data, err := ioutil.ReadFile(f)
  72. if err != nil {
  73. return err
  74. }
  75. filename := filepath.Base(f) // xxx.lua
  76. name := strings.Split(filename, ".")[0] //xxx
  77. return self.LoadString(name, string(data))
  78. }
  79. func (self *LuaScripts) LoadPath(p string) error {
  80. err := filepath.Walk(p, func(path string, info os.FileInfo, err error) error {
  81. if err != nil {
  82. return err
  83. }
  84. fi, err := os.Stat(path)
  85. if err != nil {
  86. return err
  87. }
  88. if fi.IsDir() {
  89. return nil
  90. }
  91. return self.LoadFile(path)
  92. })
  93. return err
  94. }