需求背景
在用 gin 开发网站的时候,每个页面都需要相同的页眉和页脚。 里面有些变量是通用的,例如:
- 公司 logo
- 电话
- 公司名称
- 备案号
等等。
但是,每次都在 controller 里从数据库里读取这些配置,有两个弊端:
- 每个页面的 controller 都需要重复写这个配置参数传入逻辑,啰嗦
- 这些配置极少变化,每次都读取数据库,没有必要。可以通过缓存来优化
实现逻辑
- 增加一个 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