码字不易,转载请附原链,搬砖繁忙回复不及时见谅,技术交流请加QQ群:909211071
完整最新源码:https://github.com/why444216978/gin-api/blob/master/libraries/redis/redis.go
安装redigo扩展
go get -v github.com/gomodule/redigo/redis
package redis
import (
"context"
"fmt"
"github.com/opentracing/opentracing-go"
"strconv"
"strings"
"time"
"why/config"
"why/log"
"why/util"
"github.com/gomodule/redigo/redis"
)
type RedisDB struct {
pool *redis.Pool
Config *Config
}
var obj map[string]*RedisDB
func Conn(conn string) (db *RedisDB, err error) {
if len(obj) == 0 {
obj = make(map[string]*RedisDB)
}
if obj[conn] != nil {
db = obj[conn]
return
}
fileCfg := config.GetConfigEntrance("redis", conn)
portCfg, err := strconv.Atoi(fileCfg["port"]);
dbCfg, err := strconv.Atoi(fileCfg["db"]);
maxActiveCfg, err := strconv.Atoi(fileCfg["max_active"]);
maxIdleCfg, err := strconv.Atoi(fileCfg["max_idle"]);
logCfg, err := strconv.ParseBool(fileCfg["is_log"])
util.Must(err)
cfg := &Config{
Host: fileCfg["host"],
Port: portCfg,
Password: fileCfg["auth"],
DB: dbCfg,
MaxActive: maxActiveCfg,
MaxIdle: maxIdleCfg,
IsLog: logCfg,
}
db = new(RedisDB)
db.Config = cfg
db.pool = &redis.Pool{
Dial: func() (redis.Conn, error) {
return redis.Dial(
"tcp",
fmt.Sprintf("%s:%d", cfg.Host, cfg.Port),
redis.DialPassword(cfg.Password),
redis.DialDatabase(cfg.DB),
redis.DialConnectTimeout(time.Second*2),
redis.DialReadTimeout(time.Second*2),
redis.DialWriteTimeout(time.Second*2),
)
},
TestOnBorrow: func(c redis.Conn, t time.Time) error {
_, err := c.Do("PING")
return err
},
MaxIdle: cfg.MaxIdle, // 最大的空闲连接数,表示即使没有redis连接时依然可以保持N个空闲的连接,而不被清除,随时处于待命状态
MaxActive: cfg.MaxActive, // 最大的激活连接数,表示同时最多有N个连接 ,为0事表示没有限制
IdleTimeout: time.Second, //最大的空闲连接等待时间,超过此时间后,空闲连接将被关闭
Wait: true, // 当链接数达到最大后是否阻塞,如果不的话,达到最大后返回错误
}
if _, err = db.Do(context.TODO(), "PING"); err != nil {
fmt.Println(err)
err = errorsWrap(err, "ping error")
return
}
obj[conn] = db
return
}
func errorsWrap(err error, msg string) error {
return fmt.Errorf("%s: %w", msg, err)
}
// ConnPool 返回 redis.Pool.
// 除非必要一般不建议用这个函数, 用本库封装好的函数操作数据库.
func (db *RedisDB) ConnPool() *redis.Pool {
return db.pool
}
// Close 释放连接资源.
func (db *RedisDB) Close() error {
if db.pool != nil {
return db.pool.Close()
}
return nil
}
// Do 执行 redis 命令
// NOTE 除非有必要(比如在一个函数内部需要执行多次 redis 操作), 否则请用该函数执行所有的操作, 这样能有效避免忘记释放资源.
func (db *RedisDB) Do(ctx context.Context, commandName string, args ...interface{}) (reply interface{}, err error) {
var (
conn = db.pool.Get()
parent = opentracing.SpanFromContext(ctx)
span opentracing.Span
startAt = time.Now()
endAt time.Time
logFormat = log.LogHeaderFromContext(ctx)
argsStr []string
)
defer conn.Close()
reply, err = conn.Do(commandName, args...)
if db.Config.IsLog == false {
return;
}
if logFormat == nil {
logFormat = log.NewLog()
}
for _, arg := range args {
argsStr = append(argsStr, fmt.Sprint(arg))
}
lastModule := logFormat.Module
defer func() {logFormat.Module = lastModule}()
defer func() {
endAt = time.Now()
logFormat.StartTime = startAt
logFormat.EndTime = endAt
latencyTime := logFormat.EndTime.Sub(logFormat.StartTime).Microseconds()// 执行时间
logFormat.LatencyTime = latencyTime
logFormat.Module = "databus/redis"
if endAt.Sub(startAt) > db.Config.ExecTimeout.Duration {
log.Warnf(logFormat, "redis do:[%s], used: %d milliseconds",
fmt.Sprint(commandName, " ", strings.Join(argsStr, " ")),
endAt.Sub(startAt).Milliseconds())
}
if err != nil {
log.Errorf(logFormat, "redis do:[%s], error: %s",
fmt.Sprint(commandName, " ", strings.Join(argsStr, " ")),
err)
}
}()
if parent == nil {
span = opentracing.StartSpan("redisDo")
} else {
span = opentracing.StartSpan("redisDo", opentracing.ChildOf(parent.Context()))
}
defer span.Finish()
span.SetTag("db.type", "redis")
span.SetTag("db.statement", fmt.Sprint(commandName, " ", strings.Join(argsStr, " ")))
span.SetTag("error", err != nil)
return
}
使用:
package services
import (
"context"
"strconv"
"why/util"
redigo "github.com/gomodule/redigo/redis"
)
const (
location_detail = "location::id_detail:"
location_name = "location::id_name:"
)
func GetLocationDetail(ctx context.Context, id int) string {
db := Conn("location")
data, err := redigo.String( db.Do(ctx, "GET", location_name + strconv.Itoa(id)) )
util.Must(err)
return data
}
func BatchLocationDetail(ctx context.Context, ids []int) []string {
db := Conn("location")
var args []interface{}
for _,v := range ids {
args = append(args, location_detail + strconv.Itoa(v))
}
data, err := redigo.Strings(db.Do(ctx,"MGET", args...))
util.Must(err)
return data
}
config参考:https://blog.csdn.net/why444216978/article/details/103978355
util参考:https://blog.csdn.net/why444216978/article/details/103992579