【问题标题】:Calling lua script from redigo throwing error wrong number of args从redigo调用lua脚本抛出错误的args数量错误
【发布时间】:2017-01-29 07:54:02
【问题描述】:

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

package main

import (
    "github.com/PuerkitoBio/redisc"
    "github.com/garyburd/redigo/redis"
    "log"
    "time"
)

const script = `if redis.call("EXISTS", KEYS[1]) == 1 then
    local keyvalues = redis.call("HGETALL", KEYS[1])
    local a = {}
    for i=2, table.getn(ARGV) do
      a[i-1] = ARGV[i]
    end
    local res = redis.call("HMSET", KEYS[1], unpack(a))
    redis.call("EXPIRE", KEYS[1], ARGV[1])  
    return keyvalues
else
    return 2 -- "Key doesn't exists"
end`

func main() {
    cluster := redisc.Cluster{
        StartupNodes: []string{":30001", ":30002", ":30003", ":30004", ":30005", ":30006"},
        DialOptions:  []redis.DialOption{redis.DialConnectTimeout(5 * time.Second)},
        CreatePool:   createPool,
    }
    defer cluster.Close()

    // initialize its mapping
    if err := cluster.Refresh(); err != nil {
        log.Fatalf("Refresh failed: %v", err)
    }

    // grab a connection from the pool
    conn := cluster.Get()
    defer cluster.Close()
    retryConn, errRe := redisc.RetryConn(conn, 3, 1*time.Millisecond)
    if errRe != nil {
        log.Println("Failed to get retry connection " + errRe.Error())
        return
    }
    rScript := redis.NewScript(1, script)
    argv := make([]string, 5)
    argv[0] = "30000"
    argv[1] = "SSF_lastAccessedDate"
    argv[2] = "1481627386"
    argv[3] = "SSF_expiryDate"
    argv[4] = "2481657386"
    reply, errS := rScript.Do(retryConn, "JJNb324a680c35d11e6a1123c15c2d271f21481871788G", argv)
    if errS != nil {
        log.Println("Error in executing script " + errS.Error())
    } else {
        log.Printf("Result %+v", reply)
    }
}

func createPool(addr string, opts ...redis.DialOption) (*redis.Pool, error) {
    return &redis.Pool{
        MaxIdle:     100,
        MaxActive:   4000,
        IdleTimeout: time.Minute,
        Dial: func() (redis.Conn, error) {
            return redis.Dial("tcp", addr, opts...)
        },
        TestOnBorrow: func(c redis.Conn, t time.Time) error {
            if time.Since(t) < time.Minute {
                return nil
            }
            _, err := c.Do("PING")
            return err
        },
    }, nil
}

但是在执行代码时它会抛出错误:-

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 

有人可以告诉我出了什么问题以及如何使这项工作正常进行吗?

环境:-

  • 开始 - 1.7.4

编辑

按照答案中的建议,我将调用修改为:-

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

但它给出的输出为:-

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) "type"
 2) "2"
 3) "data"
 4) "Hello"
 5) "x"
 6) "2"
 7) "SSF_creationDate"
 8) "1481871788"
 9) "SSF_lastModifiedDate"
10) "1481871788"
11) "SSF_expiryDate"
12) "2481657386"
13) "SSF_lastAccessedDate"
14) "1481627386"

【问题讨论】:

    标签: go lua redis redigo


    【解决方案1】:

    根据报错信息,错误在这一行:

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

    问题是应用程序将两个参数传递给脚本,密钥和argv 转换为字符串。调用 unpack(a) 产生一个值。

    解决方法是将每个参数分别传递给脚本:

    reply, errS := rScript.Do(retryConn,
       "JJNb324a680c35d11e6a1123c15c2d271f21481871788G",
       "30000",
       "SSF_lastAccessedDate", "1481627386",
       "SSF_expiryDate", "2481657386")
    

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

    var rScript = redis.NewScript(1, `if redis.call("EXISTS", KEYS[1]) == 1 then
        local keyvalues = redis.call("HGETALL", KEYS[1])
        local a = {}
        for i=2, table.getn(ARGV) do
          a[i-1] = ARGV[i]
        end
        local res = redis.call("HMSET", KEYS[1], unpack(a))
        redis.call("EXPIRE", KEYS[1], ARGV[1])  
        return keyvalues
    else
        return 2 -- "Key doesn't exists"
    end`)
    

    并在main() 中使用此脚本:

    func main() {
      ...
      reply, errS := rScript.Do(retryConn,
       "JJNb324a680c35d11e6a1123c15c2d271f21481871788G",
       "30000",
       "SSF_lastAccessedDate", "1481627386",
       "SSF_expiryDate", "2481657386")
    
      ...
    }
    

    另外,使用 table.remove 来简化脚本:

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

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

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

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

    【讨论】:

    • 我按照您的建议修改了对 rScript.Do 的调用,但输出显示为 2016/12/16 21:50:08 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]]
    【解决方案2】:

    你没有正确使用Do 方法,如果你想在脚本中使用它们,你必须分别给它每个变量:

    rScript := redis.NewScript(1, script)
    argv := make([]string, 6)
    argv[0] = "JJNb324a680c35d11e6a1123c15c2d271f21481871788G" // the key
    argv[1] = "30000"
    argv[2] = "SSF_lastAccessedDate"
    argv[3] = "1481627386"
    argv[4] = "SSF_expiryDate"
    argv[5] = "2481657386"
    reply, err := rScript.Do(con, argv...)
    if err != nil {
        log.Println("Error in executing script " + err.Error())
    } else {
        log.Printf("Result %v\n", reply)
        // error if not return a map
        if data, err := redis.StringMap(reply, nil); err != nil {
            // error if not return an int
            if val, err := redis.Int64(reply, nil); err != nil {
                log.Printf("Error %v\n", err)
            } else {
                log.Printf("Result %v\n", val)
            }
        } else {
            log.Printf("Result %v\n", data)
        }
    }
    

    【讨论】:

    • 要修复编译错误,请将argv := make([]string, 6)更改为argv := make([]interface{}, 6)
    【解决方案3】:

    你应该开始使用

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

    而不是

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-08-02
      • 2012-04-08
      • 1970-01-01
      • 1970-01-01
      • 2015-09-09
      • 2015-06-04
      • 1970-01-01
      相关资源
      最近更新 更多