调用lua脚本时,使用redigo抛出错误,参数数量错误。

huangapple go评论122阅读模式
英文:

Calling lua script from redigo throwing error wrong number of args

问题

我正在尝试使用redigo在Redis上执行一个Lua脚本。我的代码如下所示:

  1. package main
  2. import (
  3. "github.com/PuerkitoBio/redisc"
  4. "github.com/garyburd/redigo/redis"
  5. "log"
  6. "time"
  7. )
  8. const script = `if redis.call("EXISTS", KEYS[1]) == 1 then
  9. local keyvalues = redis.call("HGETALL", KEYS[1])
  10. local a = {}
  11. for i=2, table.getn(ARGV) do
  12. a[i-1] = ARGV[i]
  13. end
  14. local res = redis.call("HMSET", KEYS[1], unpack(a))
  15. redis.call("EXPIRE", KEYS[1], ARGV[1])
  16. return keyvalues
  17. else
  18. return 2 -- "Key doesn't exists"
  19. end`
  20. func main() {
  21. cluster := redisc.Cluster{
  22. StartupNodes: []string{":30001", ":30002", ":30003", ":30004", ":30005", ":30006"},
  23. DialOptions: []redis.DialOption{redis.DialConnectTimeout(5 * time.Second)},
  24. CreatePool: createPool,
  25. }
  26. defer cluster.Close()
  27. // initialize its mapping
  28. if err := cluster.Refresh(); err != nil {
  29. log.Fatalf("Refresh failed: %v", err)
  30. }
  31. // grab a connection from the pool
  32. conn := cluster.Get()
  33. defer cluster.Close()
  34. retryConn, errRe := redisc.RetryConn(conn, 3, 1*time.Millisecond)
  35. if errRe != nil {
  36. log.Println("Failed to get retry connection " + errRe.Error())
  37. return
  38. }
  39. rScript := redis.NewScript(1, script)
  40. argv := make([]string, 5)
  41. argv[0] = "30000"
  42. argv[1] = "SSF_lastAccessedDate"
  43. argv[2] = "1481627386"
  44. argv[3] = "SSF_expiryDate"
  45. argv[4] = "2481657386"
  46. reply, errS := rScript.Do(retryConn, "JJNb324a680c35d11e6a1123c15c2d271f21481871788G", argv)
  47. if errS != nil {
  48. log.Println("Error in executing script " + errS.Error())
  49. } else {
  50. log.Printf("Result %+v", reply)
  51. }
  52. }
  53. func createPool(addr string, opts ...redis.DialOption) (*redis.Pool, error) {
  54. return &redis.Pool{
  55. MaxIdle: 100,
  56. MaxActive: 4000,
  57. IdleTimeout: time.Minute,
  58. Dial: func() (redis.Conn, error) {
  59. return redis.Dial("tcp", addr, opts...)
  60. },
  61. TestOnBorrow: func(c redis.Conn, t time.Time) error {
  62. if time.Since(t) < time.Minute {
  63. return nil
  64. }
  65. _, err := c.Do("PING")
  66. return err
  67. },
  68. }, nil
  69. }

但是在执行代码时,它会抛出以下错误:

  1. 2016/12/16 12:52:32 Error in executing script ERR Error running script (call to f_5a127779e5c1c2daa0b636d9b02f6b570a9f7f13): @user_script:7: @user_script: 7: Wrong number of args calling Redis command From Lua script

有人可以告诉我出了什么问题,以及如何使其工作吗?

环境:

  • Go - 1.7.4

编辑

根据答案的建议,我将调用修改为:

  1. rScript.Do(retryConn, "JJNb324a680c35d11e6a1123c15c2d271f21481871788G", argv[0], argv[1], argv[2], argv[3], argv[4])

但是它的输出是:

  1. 2016/12/16 21:39:51 Result [[116 121 112 101] [50] [100 97 116 97] [72 101 108 108 111] [120] [50] [83 83 70 95 99 114 101 97 116 105 111 110 68 97 116 101] [49 52 56 49 56 55 49 55 56 56] [83 83 70 95 108 97 115 116 77 111 100 105 102 105 101 100 68 97 116 101] [49 52 56 49 56 55 49 55 56 56] [83 83 70 95 101 120 112 105 114 121 68 97 116 101] [50 52 56 49 54 53 55 51 56 54] [83 83 70 95 108 97 115 116 65 99 99 101 115 115 101 100 68 97 116 101] [49 52 56 49 54 50 55 51 56 54]]

但是使用redis-cli命令,我得到的输出是:

  1. 1) "type"
  2. 2) "2"
  3. 3) "data"
  4. 4) "Hello"
  5. 5) "x"
  6. 6) "2"
  7. 7) "SSF_creationDate"
  8. 8) "1481871788"
  9. 9) "SSF_lastModifiedDate"
  10. 10) "1481871788"
  11. 11) "SSF_expiryDate"
  12. 12) "2481657386"
  13. 13) "SSF_lastAccessedDate"
  14. 14) "1481627386"
英文:

I am trying to use redigo to execute a lua script on redis. My code looks like below:-

  1. package main
  2. import (
  3. &quot;github.com/PuerkitoBio/redisc&quot;
  4. &quot;github.com/garyburd/redigo/redis&quot;
  5. &quot;log&quot;
  6. &quot;time&quot;
  7. )
  8. const script = `if redis.call(&quot;EXISTS&quot;, KEYS[1]) == 1 then
  9. local keyvalues = redis.call(&quot;HGETALL&quot;, KEYS[1])
  10. local a = {}
  11. for i=2, table.getn(ARGV) do
  12. a[i-1] = ARGV[i]
  13. end
  14. local res = redis.call(&quot;HMSET&quot;, KEYS[1], unpack(a))
  15. redis.call(&quot;EXPIRE&quot;, KEYS[1], ARGV[1])
  16. return keyvalues
  17. else
  18. return 2 -- &quot;Key doesn&#39;t exists&quot;
  19. end`
  20. func main() {
  21. cluster := redisc.Cluster{
  22. StartupNodes: []string{&quot;:30001&quot;, &quot;:30002&quot;, &quot;:30003&quot;, &quot;:30004&quot;, &quot;:30005&quot;, &quot;:30006&quot;},
  23. DialOptions: []redis.DialOption{redis.DialConnectTimeout(5 * time.Second)},
  24. CreatePool: createPool,
  25. }
  26. defer cluster.Close()
  27. // initialize its mapping
  28. if err := cluster.Refresh(); err != nil {
  29. log.Fatalf(&quot;Refresh failed: %v&quot;, err)
  30. }
  31. // grab a connection from the pool
  32. conn := cluster.Get()
  33. defer cluster.Close()
  34. retryConn, errRe := redisc.RetryConn(conn, 3, 1*time.Millisecond)
  35. if errRe != nil {
  36. log.Println(&quot;Failed to get retry connection &quot; + errRe.Error())
  37. return
  38. }
  39. rScript := redis.NewScript(1, script)
  40. argv := make([]string, 5)
  41. argv[0] = &quot;30000&quot;
  42. argv[1] = &quot;SSF_lastAccessedDate&quot;
  43. argv[2] = &quot;1481627386&quot;
  44. argv[3] = &quot;SSF_expiryDate&quot;
  45. argv[4] = &quot;2481657386&quot;
  46. reply, errS := rScript.Do(retryConn, &quot;JJNb324a680c35d11e6a1123c15c2d271f21481871788G&quot;, argv)
  47. if errS != nil {
  48. log.Println(&quot;Error in executing script &quot; + errS.Error())
  49. } else {
  50. log.Printf(&quot;Result %+v&quot;, reply)
  51. }
  52. }
  53. func createPool(addr string, opts ...redis.DialOption) (*redis.Pool, error) {
  54. return &amp;redis.Pool{
  55. MaxIdle: 100,
  56. MaxActive: 4000,
  57. IdleTimeout: time.Minute,
  58. Dial: func() (redis.Conn, error) {
  59. return redis.Dial(&quot;tcp&quot;, addr, opts...)
  60. },
  61. TestOnBorrow: func(c redis.Conn, t time.Time) error {
  62. if time.Since(t) &lt; time.Minute {
  63. return nil
  64. }
  65. _, err := c.Do(&quot;PING&quot;)
  66. return err
  67. },
  68. }, nil
  69. }

But on executing the code it is throwing the error:-

  1. 2016/12/16 12:52:32 Error in executing script ERR Error running script (call to f_5a127779e5c1c2daa0b636d9b02f6b570a9f7f13): @user_script:7: @user_script: 7: Wrong number of args calling Redis command From Lua script

Can someone let me know what is going wrong & how to make this work?

Environment:-

  • Go - 1.7.4

EDIT

As suggested in the answers I modified the call as:-

  1. rScript.Do(retryConn, &quot;JJNb324a680c35d11e6a1123c15c2d271f21481871788G&quot;, argv[0], argv[1], argv[2], argv[3], argv[4])

But it is giving the output as:-

  1. 2016/12/16 21:39:51 Result [[116 121 112 101] [50] [100 97 116 97] [72 101 108 108 111] [120] [50] [83 83 70 95 99 114 101 97 116 105 111 110 68 97 116 101] [49 52 56 49 56 55 49 55 56 56] [83 83 70 95 108 97 115 116 77 111 100 105 102 105 101 100 68 97 116 101] [49 52 56 49 56 55 49 55 56 56] [83 83 70 95 101 120 112 105 114 121 68 97 116 101] [50 52 56 49 54 53 55 51 56 54] [83 83 70 95 108 97 115 116 65 99 99 101 115 115 101 100 68 97 116 101] [49 52 56 49 54 50 55 51 56 54]]

But from redis-cli I am getting the output as:-

  1. 1) &quot;type&quot;
  2. 2) &quot;2&quot;
  3. 3) &quot;data&quot;
  4. 4) &quot;Hello&quot;
  5. 5) &quot;x&quot;
  6. 6) &quot;2&quot;
  7. 7) &quot;SSF_creationDate&quot;
  8. 8) &quot;1481871788&quot;
  9. 9) &quot;SSF_lastModifiedDate&quot;
  10. 10) &quot;1481871788&quot;
  11. 11) &quot;SSF_expiryDate&quot;
  12. 12) &quot;2481657386&quot;
  13. 13) &quot;SSF_lastAccessedDate&quot;
  14. 14) &quot;1481627386&quot;

答案1

得分: 1

你没有正确使用Do方法,如果你想在脚本中使用变量,必须将每个变量单独传递给它:

  1. rScript := redis.NewScript(1, script)
  2. argv := make([]string, 6)
  3. argv[0] = "JJNb324a680c35d11e6a1123c15c2d271f21481871788G" // the key
  4. argv[1] = "30000"
  5. argv[2] = "SSF_lastAccessedDate"
  6. argv[3] = "1481627386"
  7. argv[4] = "SSF_expiryDate"
  8. argv[5] = "2481657386"
  9. reply, err := rScript.Do(con, argv...)
  10. if err != nil {
  11. log.Println("Error in executing script " + err.Error())
  12. } else {
  13. log.Printf("Result %v\n", reply)
  14. // error if not return a map
  15. if data, err := redis.StringMap(reply, nil); err != nil {
  16. // error if not return an int
  17. if val, err := redis.Int64(reply, nil); err != nil {
  18. log.Printf("Error %v\n", err)
  19. } else {
  20. log.Printf("Result %v\n", val)
  21. }
  22. } else {
  23. log.Printf("Result %v\n", data)
  24. }
  25. }
英文:

You're not using Do method correctly, you must to give it every variable separately if you want to use them in the script:

  1. rScript := redis.NewScript(1, script)
  2. argv := make([]string, 6)
  3. argv[0] = &quot;JJNb324a680c35d11e6a1123c15c2d271f21481871788G&quot; // the key
  4. argv[1] = &quot;30000&quot;
  5. argv[2] = &quot;SSF_lastAccessedDate&quot;
  6. argv[3] = &quot;1481627386&quot;
  7. argv[4] = &quot;SSF_expiryDate&quot;
  8. argv[5] = &quot;2481657386&quot;
  9. reply, err := rScript.Do(con, argv...)
  10. if err != nil {
  11. log.Println(&quot;Error in executing script &quot; + err.Error())
  12. } else {
  13. log.Printf(&quot;Result %v\n&quot;, reply)
  14. // error if not return a map
  15. if data, err := redis.StringMap(reply, nil); err != nil {
  16. // error if not return an int
  17. if val, err := redis.Int64(reply, nil); err != nil {
  18. log.Printf(&quot;Error %v\n&quot;, err)
  19. } else {
  20. log.Printf(&quot;Result %v\n&quot;, val)
  21. }
  22. } else {
  23. log.Printf(&quot;Result %v\n&quot;, data)
  24. }
  25. }

答案2

得分: 1

根据错误信息,错误出现在以下代码行:

  1. local res = redis.call("HMSET", KEYS[1], unpack(a))

问题在于应用程序将两个参数传递给脚本,即键和将argv转换为字符串。调用unpack(a)只返回一个值。

修复方法是将每个参数单独传递给脚本:

  1. reply, errS := rScript.Do(retryConn,
  2. "JJNb324a680c35d11e6a1123c15c2d271f21481871788G",
  3. "30000",
  4. "SSF_lastAccessedDate", "1481627386",
  5. "SSF_expiryDate", "2481657386")

我建议使用包级别变量初始化脚本,这样脚本文本的哈希值只需计算一次,而不是在每次使用脚本时都计算:

  1. var rScript = redis.NewScript(1, `if redis.call("EXISTS", KEYS[1]) == 1 then
  2. local keyvalues = redis.call("HGETALL", KEYS[1])
  3. local a = {}
  4. for i=2, table.getn(ARGV) do
  5. a[i-1] = ARGV[i]
  6. end
  7. local res = redis.call("HMSET", KEYS[1], unpack(a))
  8. redis.call("EXPIRE", KEYS[1], ARGV[1])
  9. return keyvalues
  10. else
  11. return 2 -- "Key doesn't exist"
  12. end`)

并在main()函数中使用该脚本:

  1. func main() {
  2. ...
  3. reply, errS := rScript.Do(retryConn,
  4. "JJNb324a680c35d11e6a1123c15c2d271f21481871788G",
  5. "30000",
  6. "SSF_lastAccessedDate", "1481627386",
  7. "SSF_expiryDate", "2481657386")
  8. ...
  9. }

此外,使用table.remove可以简化脚本:

  1. var rScript = redis.NewScript(1, `if redis.call("EXISTS", KEYS[1]) == 1 then
  2. local keyvalues = redis.call("HGETALL", KEYS[1])
  3. local expires = table.remove(ARGV, 1)
  4. local res = redis.call("HMSET", KEYS[1], unpack(ARGV))
  5. redis.call("EXPIRE", KEYS[1], expires)
  6. return keyvalues
  7. else
  8. return 2 -- "Key doesn't exist"
  9. end`)

使用redis.Strings将从服务器返回的[]byte切片转换为字符串切片:

  1. reply, errS := redis.Strings(rScript.Do(retryConn,
  2. "JJNb324a680c35d11e6a1123c15c2d271f21481871788G",
  3. "30000",
  4. "SSF_lastAccessedDate", "1481627386",
  5. "SSF_expiryDate", "2481657386"))

打印reply将得到您期望的结果。

英文:

According to the error message, the error is on this line:

  1. local res = redis.call(&quot;HMSET&quot;, KEYS[1], unpack(a))

The issue is that the application is passing two arguments to the script, the key and argv converted to a string. The call unpack(a) yields a single value.

The fix is to pass each argument separately to the script:

  1. reply, errS := rScript.Do(retryConn,
  2. &quot;JJNb324a680c35d11e6a1123c15c2d271f21481871788G&quot;,
  3. &quot;30000&quot;,
  4. &quot;SSF_lastAccessedDate&quot;, &quot;1481627386&quot;,
  5. &quot;SSF_expiryDate&quot;, &quot;2481657386&quot;)

I recommend initializing a package level variable with the script so the hash of the script text is computed once instead of on every use of the script:

  1. var rScript = redis.NewScript(1, `if redis.call(&quot;EXISTS&quot;, KEYS[1]) == 1 then
  2. local keyvalues = redis.call(&quot;HGETALL&quot;, KEYS[1])
  3. local a = {}
  4. for i=2, table.getn(ARGV) do
  5. a[i-1] = ARGV[i]
  6. end
  7. local res = redis.call(&quot;HMSET&quot;, KEYS[1], unpack(a))
  8. redis.call(&quot;EXPIRE&quot;, KEYS[1], ARGV[1])
  9. return keyvalues
  10. else
  11. return 2 -- &quot;Key doesn&#39;t exists&quot;
  12. end`)

and use this script in main():

  1. func main() {
  2. ...
  3. reply, errS := rScript.Do(retryConn,
  4. &quot;JJNb324a680c35d11e6a1123c15c2d271f21481871788G&quot;,
  5. &quot;30000&quot;,
  6. &quot;SSF_lastAccessedDate&quot;, &quot;1481627386&quot;,
  7. &quot;SSF_expiryDate&quot;, &quot;2481657386&quot;)
  8. ...
  9. }

Also, use table.remove to simplify the script:

  1. var rScript = redis.NewScript(1, `if redis.call(&quot;EXISTS&quot;, KEYS[1]) == 1 then
  2. local keyvalues = redis.call(&quot;HGETALL&quot;, KEYS[1])
  3. local expires = table.remove(ARGV, 1)
  4. local res = redis.call(&quot;HMSET&quot;, KEYS[1], unpack(ARGV))
  5. redis.call(&quot;EXPIRE&quot;, KEYS[1], expires)
  6. return keyvalues
  7. else
  8. return 2 -- &quot;Key doesn&#39;t exists&quot;
  9. end`)

Use redis.Strings to convert the slice of []byte returned from the server to a slice of strings:

  1. reply, errS := redis.Strings(rScript.Do(retryConn,
  2. &quot;JJNb324a680c35d11e6a1123c15c2d271f21481871788G&quot;,
  3. &quot;30000&quot;,
  4. &quot;SSF_lastAccessedDate&quot;, &quot;1481627386&quot;,
  5. &quot;SSF_expiryDate&quot;, &quot;2481657386&quot;))

Printing this reply will give the result you are expecting.

答案3

得分: 0

你应该使用args[1]、args[2]、args[3]、args[4]、args[5],而不是args[0]、args[1]、args[2]、args[3]、args[4]

英文:

You should begin use

args[1],args[2],args[3],args[4],args[5]

instead of

  1. args[0],args[1],args[2],args[3],args[4]

huangapple
  • 本文由 发表于 2016年12月16日 15:41:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/41179478.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定