关于go:golua实现redis事务

38次阅读

共计 2597 个字符,预计需要花费 7 分钟才能阅读完成。

对于事物在这就不多做概念性的介绍,本人能够去搜寻。

对于 Redis 的事务简略阐明一下

  • 事务作为一个整体被执行,执行期间不会被其它客户端申请打断
  • 事务中的多个命令打包执行,当做一个整体执行,不会被打断
  • 事务在执行的中途遇到谬误,不会回滚,而是继续执行后续命令
  • 事务中能够交叉查问,依据后果执行不同操作

咱们能够用 lua 脚本来实现事务处理

在 go 中应用 lua 脚本拜访 redis 时以下几点要留神

  • 脚本返回 nil 时,Go 中失去的是 err = redis.Nil(与 Get 找不到值雷同)
  • 脚本返回 false 时,Go 中失去的是 nil,脚本返回 true 时,Go 中失去的是 int64 类型的 1
  • 脚本返回 number 类型时,Go 中失去的是 int64 类型
  • 传入脚本的 KEYS/ARGV 中的值一律为 string 类型,要转换为数字类型应用 to_number
  • 脚本返回 {“ok”: …} 时,Go 中失去的是 redis 的 status 类型(true/false)
  • 脚本返回 {“err”: …} 时,Go 中失去的是 err 值,也能够通过 return redis.error_reply(“My Error”)达成

测试代码

cache.go

package rds

import (
    "fmt"
    "time"

    rds "github.com/gomodule/redigo/redis"
)

type Cache struct {pool *rds.Pool}

func InitCache(rdsAddress string, pwd string) *Cache {
    p := &rds.Pool{
        MaxIdle:     5,
        MaxActive:   30,
        IdleTimeout: 240 * time.Second,
        Dial: func() (rds.Conn, error) {return rds.Dial("tcp", rdsAddress, rds.DialPassword(pwd))
        },
    }

    return &Cache{pool: p,}
}

func (cache *Cache) GetCon() rds.Conn {return cache.pool.Get()
}

func (cache *Cache) Close() {cache.pool.Close()
}

func (cache *Cache) GetAndAdd(roomId string, usrId string) (usrAccPoint string, err error) {

    script := rds.NewScript(2, `
        local key1 = "usr:accpoint:" .. KEYS[2]
        local accPoint = redis.call("get", key1)

        key1 = "room:accpoints:" .. KEYS[1]
        redis.call("sadd", key1, accPoint)

        key1 = "room:accpoint:" .. KEYS[1]
        key1 = key1 .. ":" .. accPoint
        redis.call("sadd", key1, KEYS[2])

        return accPoint
    `)
    con := cache.GetCon()
    defer con.Close()

    usrAccPoint, err = rds.String(script.Do(con, roomId, usrId))
    if err != nil {fmt.Println(err.Error())
        return
    }

    return
}

func (cache *Cache) GetAndRem(roomId string, usrId string) (cnt int64, err error) {

    script := rds.NewScript(2, `
        local key1 = "usr:accpoint:" .. KEYS[2]
        local accPoint = redis.call("get", key1)

        key1 = "room:accpoint:" .. KEYS[1] .. ":" .. accPoint
        redis.call("srem", key1, KEYS[2])

        local cnt = redis.call("scard", key1)
        if (cnt == 0) then
            key1 = "room:accpoints:" .. KEYS[1]
            redis.call("srem", key1, accPoint)
        end

        return cnt
    `)
    con := cache.GetCon()
    defer con.Close()

    accPointCnt, err := rds.Int64(script.Do(con, roomId, usrId))
    if err != nil {fmt.Println(err.Error())
        return -1, err
    }

    fmt.Printf("Room:%s, remains %d accPoint\n", roomId, accPointCnt)

    return accPointCnt, nil
}

main.go

package main

import (
    "fmt"

    cache "jianghu.com/godemo/026redislua/rds"
)

const (REDIS_ADDR string = "127.0.0.1:6379")

func main() {c := cache.InitCache(REDIS_ADDR, "")
    defer c.Close()

    v, err := c.GetAndAdd("1", "100")
    if err != nil {fmt.Printf("Error:%s\n", err.Error())
    }
    fmt.Printf("Redis return v:%s\n", v)

    cnt, err := c.GetAndRem("1", "100")
    if err != nil {fmt.Printf("Error:%s\n", err.Error())
    }

    fmt.Printf("Cnt:%d\n", cnt)
}

有几个要害的办法阐明一下

(1)生成脚本

  • func rds.NewScript(keyCount int, src string) *rds.Script
    keyCount 传入参数的个数,对应 lua 脚本中 KEYS[1],KEYS[2]…KEYS[keyCount]

(2)执行脚本,如果脚本没有加载会主动调用加载解决

  • func (*rds.Script).Do(c rds.Conn, keysAndArgs …interface{}) (interface{}, error)
    返回参数须要依据类型调用相应的转换解决

正文完
 0