配置redis适用与golang云原生架构。包括redis与数据库一致性等重要内容
redis:
addr: 127.0.0.1
port: 6379
password: tiktokRedis
db: 0 # 数据库编号
var (
config = viper.Init("db")
zapLogger = zap.InitLogger()
redisOnce sync.Once
redisHelper *RedisHelper
FavoriteMutex *redsync.Mutex
RelationMutex *redsync.Mutex
)
新建
rdh := new(RedisHelper)
rdh.Client = rdb
redisHelper = rdh
在这段代码中,首先创建了一个 RedisHelper 对象,并将 rdb 赋值给其中的 Client 字段。然后,将 rdh 赋值给 redisHelper 变量。redisHelper 是一个全局变量,用于保存单例实例。
通过这样的方式,可以保证在多个并发请求中,只有第一次执行 Do() 方法时会初始化 Redis 连接单例,后续的请求都会直接返回已经初始化好的连接对象。
func NewRedisHelper() *redis.Client {
rdb := redis.NewClient(&redis.Options{
Addr: fmt.Sprintf("%s:%s", config.Viper.GetString("redis.addr"), config.Viper.GetString("redis.port")),
Password: config.Viper.GetString("redis.password"),
DB: config.Viper.GetInt("redis.db"),
DialTimeout: 10 * time.Second,
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
//MaxConnAge: 1 * time.Minute, go-redis v9 已删去
PoolSize: 10,
PoolTimeout: 30 * time.Second,
})
redisOnce.Do(func() {
rdh := new(RedisHelper)
rdh.Client = rdb
redisHelper = rdh
})
return rdb
}
初始化
func init() {
ctx := context.Background()
rdb := NewRedisHelper()
//典型测试连接的代码
if _, err := rdb.Ping(ctx).Result(); err != nil {
zapLogger.Fatalln(err.Error())
return
}
zapLogger.Info("Redis server connection successful!")
// 开启定时同步至数据库
GoCronFavorite()
GoCronRelation()
zapLogger.Info("MySQL synchronization is enabled.")
// Redis锁
// 创建Redis连接池
pool := goredis.NewPool(rdb)
// Create an instance of redisync to be used to obtain a mutual exclusion lock.
rs := redsync.New(pool)
// Obtain a new mutex by using the same name for all instances wanting the same lock.
FavoriteMutex = rs.NewMutex("mutex-favorite")
RelationMutex = rs.NewMutex("mutex-relation")
}
使用定时同步的思想,将redis中的数据定时同步到数据库中
具体做法:
func GoCronFavorite() {
s := gocron.NewSchedule()
s.Every(frequency).Tag("favoriteRedis").Seconds().Do(FavoriteMoveToDB)
s.StartAsync()
}
其中使用到的gocron:
// Schedule 定时任务 gocron
type Schedule struct {
*gocron.Scheduler
}
func NewSchedule() *gocron.Scheduler {
return gocron.NewScheduler(time.Local)
}
func RelationMoveToDB() error {
logger := zap.InitLogger()
ctx := context.Background()
keys, err := getKeys(ctx, "user::*::to_user::*::w")
if err != nil {
logger.Errorln(err)
return err
}
for _, key := range keys {
res, err := GetRedisHelper().Get(ctx, key).Result()
vSplit := strings.Split(res, "::")
_, redisAt := vSplit[0], vSplit[1]
if err != nil {
logger.Errorln(err.Error())
return err
}
// 拆分得key
kSplit := strings.Split(key, "::")
uid, tid := kSplit[1], kSplit[3]
userID, err := strconv.ParseInt(uid, 10, 64)
if err != nil {
logger.Errorln(err.Error())
return err
}
toUserID, err := strconv.ParseInt(tid, 10, 64)
if err != nil {
logger.Errorln(err.Error())
return err
}
// 检查是否存在对应ID
u, err := db.GetUserByID(ctx, userID)
if err != nil {
logger.Errorln(err.Error())
return err
}
tu, err := db.GetUserByID(ctx, toUserID)
if err != nil {
logger.Errorln(err.Error())
return err
}
if u == nil || tu == nil {
delErr := deleteKeys(ctx, key, RelationMutex)
if delErr != nil {
logger.Errorln(delErr.Error())
return delErr
}
continue
}
// 查询是否存在关注记录
relation, err := db.GetRelationByUserIDs(ctx, userID, toUserID)
if err != nil {
logger.Errorln(err.Error())
return err
} else if relation == nil && redisAt == "1" {
// 数据库中没有该关注记录,且最终状态为关注,则插入数据库
err = db.CreateRelation(ctx, userID, toUserID)
// 插入后,删除Redis中对应记录
delErr := deleteKeys(ctx, key, RelationMutex)
if delErr != nil {
logger.Errorln(delErr.Error())
return delErr
}
if err != nil {
logger.Errorln(err.Error())
return err
}
} else if relation != nil && redisAt == "2" {
// 数据库中有该关注记录,且最终状态为取消关注,则从数据库中删除该记录
err = db.DelRelationByUserIDs(ctx, userID, toUserID)
// 删除Redis中对应记录
delErr := deleteKeys(ctx, key, RelationMutex)
if delErr != nil {
logger.Errorln(delErr.Error())
return delErr
}
if err != nil {
logger.Errorln(err.Error())
return err
}
}
// 删除Redis中对应记录
delErr := deleteKeys(ctx, key, RelationMutex)
if delErr != nil {
logger.Errorln(delErr.Error())
return delErr
}
}
return nil
}