Redigo是Redis数据库的Go客户端,功能丰富,但是用起来还是不如redis-py顺畅。
GitHub: https://github.com/gomodule/redigo
以下是参照redis-py的风格封装部分命令, 是我用go重构rtfd时用到的, 支持key前缀和管道式事务:
// 封装 redigo 客户端
package db
import (
"errors"
"strings"
"time"
"github.com/gomodule/redigo/redis"
"tcw.im/ufc"
)
// DB 一个数据库连接结构
type DB struct {
// key前缀
Prefix string
pool *redis.Pool
}
// 允许适配前缀的命令
var commandsWithPrefix = []string{
"GET", "SET", "EXISTS", "DEL", "TYPE",
"RPUSH", "LPOP", "RPOP", "LLEN", "LRANGE",
"SADD", "SREM", "SISMEMBER", "SMEMBERS", "SCARD",
"HSET", "HMSET", "HGET", "HGETALL",
}
// 将key加入到v切片头部
func kpv(key string, values []string) []interface{} {
a := append([]string{key}, values...)
//converting a []string to a []interface{}
x := make([]interface{}, len(a))
for i, v := range a {
x[i] = v
}
return x
}
// New 打开一个DB连接,rawurl是redis连接串
func New(rawurl string) (c *DB, err error) {
pool := &redis.Pool{
// 最多有多少个空闲连接保留
MaxIdle: 5,
// 最多有多少活跃的连接数
MaxActive: 500,
// 空闲连接最长空闲时间
IdleTimeout: 5 * time.Minute,
// 如果超过最大连接,是报错,还是等待
Wait: true,
// 连接 redis 的函数
Dial: func() (redis.Conn, error) {
return redis.DialURL(rawurl)
},
}
return &DB{pool: pool}, nil
}
// Do 从连接池获取连接并执行命令
func (c *DB) Do(command string, args ...interface{}) (reply interface{}, err error) {
command = strings.ToUpper(command)
key := args[0].(string)
if ufc.StrInSlice(command, commandsWithPrefix) && key != "" {
args[0] = c.Prefix + key
}
rc := c.pool.Get()
defer rc.Close()
return rc.Do(command, args...)
}
// Type 查看键类型
func (c *DB) Type(key string) (string, error) {
return redis.String(c.Do("TYPE", key))
}
// Keys 查找所有符合给定模式 pattern 的 key
func (c *DB) Keys(pattern string) ([]string, error) {
return redis.Strings(c.Do("KEYS", pattern))
}
// Set 添加数据
func (c *DB) Set(key, value string) (bool, error) {
ret, err := redis.String(c.Do("SET", key, value))
if err != nil {
return false, err
}
if ret == "OK" {
return true, nil
}
return false, errors.New("set failed")
}
// Get 获取数据
func (c *DB) Get(key string) (string, error) {
return redis.String(c.Do("GET", key))
}
// Exsits 判断是否有键
func (c *DB) Exsits(key string) (bool, error) {
return redis.Bool(c.Do("EXISTS", key))
}
// Del 删除单个Key
func (c *DB) Del(key string) (bool, error) {
return redis.Bool(c.Do("DEL", key))
}
// RPush 将一个或多个值插入到列表的尾部(最右边)
func (c *DB) RPush(key string, values ...string) (uint64, error) {
args := kpv(key, values)
return redis.Uint64(c.Do("RPUSH", args...))
}
// LPop 移除并返回列表的第一个元素
func (c *DB) LPop(key string) (string, error) {
return redis.String(c.Do("LPOP", key))
}
// RPop 移除列表的最后一个元素
func (c *DB) RPop(key string) (string, error) {
return redis.String(c.Do("RPOP", key))
}
// LLen 返回列表的长度
func (c *DB) LLen(key string) (uint64, error) {
return redis.Uint64(c.Do("LLEN", key))
}
// LRange 返回列表中指定区间内的元素,区间以偏移量 START 和 END 指定
func (c *DB) LRange(key string, start, end int) ([]string, error) {
return redis.Strings(c.Do("LRANGE", key, start, end))
}
// SAdd 将一个或多个成员元素加入到集合中,已经存在于集合的成员元素将被忽略
func (c *DB) SAdd(key string, members ...string) (uint64, error) {
args := kpv(key, members)
return redis.Uint64(c.Do("SADD", args...))
}
// SRem 移除集合中的一个或多个成员元素,不存在的成员元素会被忽略
func (c *DB) SRem(key string, members ...string) (uint64, error) {
args := kpv(key, members)
return redis.Uint64(c.Do("SREM", args...))
}
// SIsMember 判断元素是否是集合的成员
func (c *DB) SIsMember(key, member string) (bool, error) {
return redis.Bool(c.Do("SISMEMBER", key, member))
}
// SMembers 返回集合中的所有的成员
func (c *DB) SMembers(key string) ([]string, error) {
return redis.Strings(c.Do("SMEMBERS", key))
}
// SCard 返回集合中元素的数量
func (c *DB) SCard(key string) (uint64, error) {
return redis.Uint64(c.Do("SCARD", key))
}
// HSet 为哈希表中的字段赋值
func (c *DB) HSet(name, key, value string) (uint64, error) {
return redis.Uint64(c.Do("HSET", name, key, value))
}
// HMSet 为哈希表中的多个字段赋值
func (c *DB) HMSet(name string, hash map[string]string) (bool, error) {
args := []interface{}{name}
for k, v := range hash {
args = append(args, k, v)
}
ret, err := c.Do("HMSET", args...)
if err != nil {
return false, err
}
if ret == "OK" {
return true, nil
}
return false, errors.New("hmset failed")
}
// HGet 返回哈希表中指定字段的值
func (c *DB) HGet(name, key string) (string, error) {
return redis.String(c.Do("HGET", name, key))
}
// HGetAll 返回哈希表中所有的字段和值
func (c *DB) HGetAll(key string) (map[string]string, error) {
return redis.StringMap(c.Do("HGETALL", key))
}
// Pipeline 开启事务,使用 Execute 方法提交事务。
// 使用示例:
//
// t := instance.Pipeline()
//
// t.Set/RPush/Del...
//
// t.Execute()
func (c *DB) Pipeline() *TranCommand {
rc := c.pool.Get()
rc.Send("MULTI")
return &TranCommand{c.Prefix, rc}
}
// TranCommand 表示事务管道
type TranCommand struct {
prefix string
conn redis.Conn
}
// Send 将命令写入客户端的输出缓冲区。
func (t *TranCommand) Send(command string, args ...interface{}) error {
command = strings.ToUpper(command)
key := args[0].(string)
if ufc.StrInSlice(command, commandsWithPrefix) && key != "" {
args[0] = t.prefix + key
}
return t.conn.Send(command, args...)
}
// Execute 执行提交事务
func (t *TranCommand) Execute() ([]interface{}, error) {
defer t.conn.Close()
return redis.Values(t.conn.Do("EXEC"))
}
// Set 管道中的 Set
func (t *TranCommand) Set(key, value string) error {
return t.Send("SET", key, value)
}
// Del 管道中的 Del
func (t *TranCommand) Del(key string) error {
return t.Send("DEL", key)
}
// RPush 管道中的 RPush
func (t *TranCommand) RPush(key string, values ...string) error {
args := kpv(key, values)
return t.Send("RPUSH", args...)
}
// SAdd 管道中的 SAdd
func (t *TranCommand) SAdd(key string, members ...string) error {
args := kpv(key, members)
return t.Send("SADD", args...)
}
// SRem 管道中的 SRem
func (t *TranCommand) SRem(key string, members ...string) error {
args := kpv(key, members)
return t.Send("SREM", args...)
}