慕仙森
实现 (Go) 如下所示:// import "github.com/gomodule/redigo/redis"type redisService struct { pool *redis.Pool lastLogin *redis.Script // Lua script initialized into this field}// Constructing a redis clientfunc NewRedisService(config *config.RedisClientConfig) RedisService { return &redisService{ pool: &redis.Pool{ MaxIdle: 10, IdleTimeout: 120 * time.Second, Dial: func() (redis.Conn, error) { return redis.Dial("tcp", config.BaseURL) }, TestOnBorrow: func(c redis.Conn, t time.Time) error { if time.Since(t) < time.Minute { return nil } _, err := c.Do("PING") return err }, }, // initialize Lua script object // lastLoginLuaScript is a Go const with the script literal lastLogin: redis.NewScript(1, lastLoginLuaScript), }}Lua脚本(注释解释了它的作用):--[[ Check if key exists, if it exists, update the value without changing the remaining TTL. If it doesn't exist, create it. Script params KEYS[1] = the account id used as key ARGV[1] = the key TTL in seconds ARGV[2] = the value]]--local errorKeyExpired = 'KEXP'local statusKeyUpdated = 'KUPD'local statusKeyCreated = 'KCRE'if redis.call('exists', KEYS[1]) == 1 then local ttl = redis.call('ttl', KEYS[1]) if ttl < 0 then --[[ no expiry ]]-- redis.call('setex', KEYS[1], ARGV[1], ARGV[2]) return redis.status_reply(statusKeyCreated) end if ttl == 0 then --[[ expired ]]-- return redis.error_reply(errorKeyExpired) end redis.call('setex', KEYS[1], ttl, ARGV[2]) return redis.status_reply(statusKeyUpdated)else redis.call('setex', KEYS[1], ARGV[1], ARGV[2]) return redis.status_reply(statusKeyCreated)end用法:func (rs *redisService) UpsertLastLoginTime(key string, ttl uint, value int64) (bool, error) { conn := rs.pool.Get() defer conn.Close() // call Do on the script object resp, err := rs.lastLogin.Do(conn, key, ttl, value) switch resp { case statusKeyCreated: return true, nil case statusKeyUpdated: return false, nil case errorKeyExpired: return false, ErrKeyExpired default: return false, errors.Wrap(err, "script execution error") }}