sync.Map 实现 gin template 全局共享变量

文章目录

    需求背景

    在用 gin 开发网站的时候,每个页面都需要相同的页眉和页脚。
    里面有些变量是通用的,例如:

    • 公司 logo
    • 电话
    • 公司名称
    • 备案号

    等等。

    但是,每次都在 controller 里从数据库里读取这些配置,有两个弊端:

    1. 每个页面的 controller 都需要重复写这个配置参数传入逻辑,啰嗦
    2. 这些配置极少变化,每次都读取数据库,没有必要。可以通过缓存来优化

    实现逻辑

    • 增加一个 template func,方便 template 中直接调用,省去 controller 中传参的步骤
    • 定义一个全局缓存,sync.Map 类型。

    实现代码

    cache.go

    langs 作为可选参数,用来实现多语言配置的实现。例如,中文 logo,英文 logo。
    如果不需要多语言支持,把这个参数去掉即可。

    var cache sync.Map
    
    func CacheGet(key string, langs ...string) any {
    	if len(langs) == 1 && langs[0] != "" {
    		key = fmt.Sprintf("%s_%s", key, langs[0])
    	}
    
    	value, found := cache.Load(key)
    	if !found {
    		// 单个 coroutine 内是顺序执行的,所以不用担心一次渲染模板导致拉取多次
    		log.Println("Fail to load data from cache. Init cache ...")
    		InitCache()
    		log.Println("Finish initing cache!")
    	}
    
    	value, _ = cache.Load(key)
    	return value
    }
    
    func InitCache() {
    	var settingList []models.Setting
    	models.DB.Find(&settingList)
    
    	for _, v := range settingList {
    		cache.Store(v.KeyName, v.Value)
    	}
    }
    

    template

    <img src={{ CacheGet "logo_white" .lang }} alt="" class="mx-auto"/>
    

    为何使用 sync.Map 而不是 Map

    主要是为了线程安全,参考这里


    https://zhuanlan.zhihu.com/p/342241598

    这个需求场景到是不关心是否线程安全,主要是多个 coroutine 同时读写一个 Map 会导致报错。只能选择 sync.Map。

    同时也发现了,几个不错的 golang gin 的缓存库,虽然最后没用上 (场景很简单,不需要引入 cache 库):

    • 用于 gin 缓存返回结果 (rendered view): https://github.com/gin-contrib/cache
    • gin cache 的改良版: https://github.com/chenyahui/gin-cache
    • go 的老牌缓存库: https://github.com/patrickmn/go-cache
    • https://github.com/allegro/bigcache

    关于作者 🌱

    我是来自山东烟台的一名开发者,有感兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊,或者关注我的个人公众号“大象工具”, 查看更多联系方式