Go项目优化——动态缓存Redis的使用
目录
- 1. Redis:
- 1.1 简介:
- 1.2 连接redis
- 1.3 常用api:
- 1.3 连接池:
- 1.4 项目中使用:
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
}
}