单例的使用

var redisClient redis.UniversalClient
var onceRedisClient = &sync.Once{}

var invokeTimes atomic.Int64 // redisClient被调用次数

// InitRedis 初始化redis客户端
// redisConnectionName redis连接名
func InitRedis(redisConnectionName string) {
    onceRedisClient.Do(func() {
        if len(redisConnectionName) == 0 {
            log.Fatalf("db.InitRedis: no connection name while create redis client")
        }
        var err error = nil
        redisClient, err = goredis.New(redisConnectionName)
        if err != nil {
            log.Fatalf("db.InitRedis: [%s]new redis client fail err=[%v]", redisConnectionName, err)
        }
    })
}

// GetRedis 获取redis客户端
func GetRedis() redis.UniversalClient {
    if redisClient == nil {
        log.Fatalf("db.GetRedis: redis client not inited")
        return nil
    }
    if times := invokeTimes.Add(1); times%1000 == 0 {
        log.Debugf("db.GetRedis: %v redis ConnPool Stats: %+v, invokeTimes: %v", &redisClient, redisClient.PoolStats(), times)
    }
    return redisClient
}

这是一段初始化redis的逻辑,因为我们无论是用数据库还是redis,都要链接redis的客户端,我们不可能使用一次方法就链接一次,所以正确方法就是这样的初始化,作为单例,然后建立连接池,然后我们调用GetRedis方法后单例就可以了

10.热加载怎么实现

// ActivityConf 活动配置
type ActivityConf struct {
	SignKey              string `yaml:"sign_key"`
	ObWarmUpActivityOpen bool   `yaml:"ob_warm_up_activity_open"`
}

var activityConf = ActivityConf{}
var activityLock sync.RWMutex

const defaultActivityConfigPath = "./conf/activity_cfg.yaml"

func init() {
	// 创建文件监听器
	fm, err := filemonitor.NewFileMonitor()
	if err != nil {
		log.Fatalf("Failed to NewFileMonitor: %v", err)
	}
	// Bind: 告诉程序 "你要盯着 ./conf/activity_cfg.yaml 这个文件"
	// Exec: 程序启动时,立刻执行一次 loadActivityConfig,保证服务刚启动就有配置数据。
	if err := fm.BindAndExec(defaultActivityConfigPath, loadActivityConfig); err != nil {
		log.Fatalf("Failed to BindAndExec: %v", err)
	}
	// 3. 开启后台协程
	// 这是一个死循环的后台任务,它利用操作系统的能力(如 Linux 的 inotify)
	// 一旦发现文件被"保存/修改"了,它就会自动调用 loadActivityConfig
	fm.StartWatch()
}

func loadActivityConfig() error {
	// 1. 读文件 (IO 操作,很慢,不需要加锁)
	// 此时还不涉及修改全局变量,所以不需要锁,别挡着读请求。
	yamlFile, err := os.ReadFile(defaultActivityConfigPath)
	if err != nil {
		log.Fatalf("读取文件失败: %v", err)
		return err
	}
	activityLock.Lock()
	defer activityLock.Unlock()
	// 这里直接修改了全局变量 activityConf 的内容,真正需要加锁的地方
	err = yaml.Unmarshal(yamlFile, &activityConf)
	if err != nil {
		log.Fatalf("解析 YAML 失败: %v", err)
		return err
	}
	log.Infof("Load tlog config: %+v", activityConf)
	return nil
}

func GetActivityConf() ActivityConf {
	activityLock.RLock()
	defer activityLock.RUnlock()
	return activityConf
}

这是一段简单的读取配置文件的热加载模式,熟悉了这个方法就可以了。