1. Redis:
1.1 简介:
garyburd/redigo 包是网上很多博文都在推荐使用的一个高Star的Redis连接包,项目已经迁移到了gomodule/redigo,同时包的获取也理所当然地改成了go get github.com/gomodule/redigo/redis,总之,暂时不管这两个包的详细区别,以下就以新包为准,介绍下redigo包使用。
1.2 连接redis
//第一种连接方法 con,err := redis.Dial("tcp", "链接地址,例如127.0.0.1:6379", redis.DialPassword("密码"), ) // or // con,err := redis.Dial("tcp","127.0.0.1:6379") if err != nil { fmt.Println(err) } defer con.Close()
1.3 常用api:
// Do 向服务器发送命令并返回收到的应答。 func Do(commandName string, args ...interface{}) (reply interface{}, err error) // reply 具体类型,具体转换 func Int(reply interface{}, err error) (int, error) func String(reply interface{}, err error) (string, error) func Bool(reply interface{}, err error) (bool, error) func Values(reply interface{}, err error) ([]interface{}, error) func Strings(reply interface{}, err error) ([]string, error) func ByteSlices(reply interface{}, err error) ([][]byte, error) func Ints(reply interface{}, err error) ([]int, error) func StringMap(result interface{}, err error) (map[string]string, error) ... // 更多函数自行探索
1.3 连接池:
在golang的项目中,若要频繁的用redis(或者其他类似的NoSQL)来存取数据,最好用redigo自带的池来管理连接。
不然的话,每当要操作redis时,建立连接,用完后再关闭,会导致大量的连接处于TIME_WAIT状态(redis连接本质上就是tcp)。
注:TIME_WAIT,也叫TCP半连接状态,会继续占用本地端口。
import ( "fmt" "github.com/gomodule/redigo/redis" "time" ) var redisPoll *redis.Pool func initRedis() { redisPoll = &redis.Pool{ // 连接方法 Dial: func() (redis.Conn, error) { c, err := redis.Dial("tcp", "链接地址,例如127.0.0.1:6379", redis.DialPassword("密码"), redis.DialReadTimeout(1*time.Second), redis.DialWriteTimeout(1*time.Second), redis.DialConnectTimeout(1*time.Second), ) if err != nil { return nil, err } return c, nil }, // 最大的空闲连接数, // 表示即使没有redis连接时依然可以保持N个空闲的连接,而不被清除,随时处于待命状态。 MaxIdle: 256, // 最大的激活连接数,表示同时最多有N个连接 MaxActive: 256, // 最大的空闲连接等待时间,超过此时间后,空闲连接将被关闭 IdleTimeout: time.Duration(120), } } // myDo // @Title myDo // @Description 封装的 redis Do函数 // Do(commandName string, args ...interface{}) (reply interface{}, err error) // @Param cmd string 命令 // @Param key interface{} 键 // @Param args ...interface{} 参数 // @Return interface{} redis服务器返回值 // @Return error 错误 func myDo(cmd string, key interface{}, args ...interface{}) (interface{}, error) { // 从pool里获取一个redis连接,如果连接池没有,会调用 Dial() con := redisPoll.Get() if err := con.Err(); err != nil { return nil, err } parmas := make([]interface{}, 0) parmas = append(parmas, key) // 如果参数不为空,也加入参数列表 if len(args) > 0 { for _, arg := range args { parmas = append(parmas, arg) } } return con.Do(cmd, parmas...) } func main() { initRedis() myDo("set", "mykey1", "myvalue1") result, err := myDo("get", "mykey1") if err != nil { fmt.Println(err.Error()) } // String()是将命令应答转换为字符串的帮助器。 str, _ := redis.String(result, err) fmt.Println(str) }
1.4 项目中使用:
dynamic cache:动态缓存
conf/dynamicache.conf
#******************* #动态缓存配置 #******************* # redis地址 dynamicache_addrstr=127.0.0.1:6379 # redis密码 dynamicache_passwd=密码
conf/app.conf
... # 引入动态缓存配置文件 include "dynamicache.conf" ...
utils/dynamic_cache.go
package dynamicache import ( "encoding/json" "github.com/astaxie/beego/logs" "strconv" "time" "github.com/astaxie/beego" "github.com/gomodule/redigo/redis" ) var ( pool *redis.Pool = nil // MaxIdle 最大的空闲连接数, MaxIdle int = 0 // MaxOpen 最大的激活连接数,表示同时最多有N个连接 MaxOpen int = 0 // ExpireSec 超时时间 ExpireSec int64 = 0 ) // InitCache 在 sysinit.go 中调用 // 初始化redis配置 func InitCache() { addr := beego.AppConfig.String("dynamicache_addrstr") if len(addr) == 0 { addr = "127.0.0.1:6379" } if MaxIdle <= 0 { MaxIdle = 256 } password := beego.AppConfig.String("dynamicache_passwd") if len(password) == 0 { pool = &redis.Pool{ MaxIdle: MaxIdle, MaxActive: MaxOpen, // 最大 IdleTimeout: time.Duration(120), Dial: func() (redis.Conn, error) { return redis.Dial( "tcp", addr, redis.DialReadTimeout(1*time.Second), redis.DialWriteTimeout(1*time.Second), redis.DialConnectTimeout(1*time.Second), ) }, } } else { pool = &redis.Pool{ MaxIdle: MaxIdle, MaxActive: MaxOpen, IdleTimeout: time.Duration(120), Dial: func() (redis.Conn, error) { return redis.Dial( "tcp", addr, redis.DialReadTimeout(1*time.Second), redis.DialWriteTimeout(1*time.Second), redis.DialConnectTimeout(1*time.Second), redis.DialPassword(password), ) }, } } }
sysinit/sysinit.go
package sysinit import ( ... "mybook/utils/dynamicache" ... ) .... // initDynamicCache // @Title initDynamicCache // @Description 初始化dynamic_cache.go里的公有值,并初始化redis配置 func initDynamicCache() { dynamicache.MaxOpen = 128 dynamicache.MaxIdle = 128 dynamicache.ExpireSec = 10 dynamicache.InitCache() } ....
根据实际业务需要,在utils/dynamic_cache.go里封装会用到的方法
utils/dynamic_cache.go
... // redisDo // @Title redisDo // @Description 封装的 redis Do函数 // Do(commandName string, args ...interface{}) (reply interface{}, err error) // @Param cmd string 命令 // @Param key interface{} 键 // @Param args ...interface{} 参数 // @Return interface{} redis服务器返回值 // @Return error 错误 func redisDo(cmd string, key interface{}, args ...interface{}) (interface{}, error) { con := pool.Get() if err := con.Err(); err != nil { return nil, err } params := make([]interface{}, 0) params = append(params, key) if len(args) > 0 { for _, v := range args { params = append(params, v) } } return con.Do(cmd, params...) } // WriteString // @Title WriteString // @Description 写字符串 // @Param key string 键 // @Param value string 值 // @Return error 错误 func WriteString(key string, value string) error { _, err := redisDo("SET", key, value) logs.Debug("redis set:" + key + "-" + value) redisDo("EXPIRE", key, ExpireSec) return err } // ReadString // @Title ReadString // @Description 读字符串 // @Param key string 键 // @Param value string 值 // @Return error 错误 func ReadString(key string) (string, error) { result, err := redisDo("GET", key) logs.Debug("redis get:" + key) if nil == err { str, _ := redis.String(result, err) return str, nil } else { logs.Debug("redis get error:" + err.Error()) return "", err } } // 以下封装的方法都是在WriteString() ReadString()上封装的,使用的都是redis里的string类型 // WriteStruct // @Title WriteStruct // @Description 写结构体(本质是还是写json字符串) func WriteStruct(key string, obj interface{}) error { data, err := json.Marshal(obj) if nil == err { return WriteString(key, string(data)) } else { return nil } } // ReadStruct // @Title ReadStruct // @Description 读结构体 func ReadStruct(key string, obj interface{}) error { if data, err := ReadString(key); nil == err { return json.Unmarshal([]byte(data), obj) } else { return err } } // WriteList // @Title WriteList // @Description 写数组 func WriteList(key string, list interface{}, total int) error { realKeyList := key + "_list" realKeyCount := key + "_count" data, err := json.Marshal(list) if nil == err { WriteString(realKeyCount, strconv.Itoa(total)) return WriteString(realKeyList, string(data)) } else { return nil } } // ReadList // @Title ReadList // @Description 读数组 func ReadList(key string, list interface{}) (int, error) { realKeyList := key + "_list" realKeyCount := key + "_count" if data, err := ReadString(realKeyList); nil == err { totalStr, _ := ReadString(realKeyCount) total := 0 if len(totalStr) > 0 { total, _ = strconv.Atoi(totalStr) } return total, json.Unmarshal([]byte(data), list) } else { return 0, err } }