一个 Gin 缓存中间件的设计与实现

2021-06-18 16:17:05 +08:00
 cyhone

我们在开发 HTTP Server 的时候,经常有对接口内容做缓存的需求。例如,对于某些热点内容,我们希望做 1 分钟内的缓存。短期内缓存相同内容不会对业务造成实质影响,同时也会降低系统的整体负载。

有时我们需要把缓存逻辑放在 Server 内部,而非网关侧如 Nginx 等,是因为这样我们可以根据需要便捷地清除缓存,或者可以使用 Redis 等其他存储介质作为缓存后端。

这样的缓存场景无非是有缓存时从缓存取,无缓存时从下游服务取,并将数据放入缓存中。这其实是个非常通用的逻辑,应该可以将其抽象出来。从而缓存逻辑无需进侵入业务代码。

我常用的 HTTP 框架是 golang 的 gin 。gin 官方就有一个 cache 组件:github.com/gin-contrib/cache,但这个 cache 组件无论在性能还是接口设计上,都有一些不足之处。

因此,我重新设计了一套 cache 中间件: gin-cache。 从压测结果来看,其性能相比于 gin-contrib/cache 明显提升。

点击查看原文>>

2046 次点击
所在节点    Go 编程语言
11 条回复
meshell
2021-06-18 16:21:02 +08:00
没看代码。问个问题,比如说如果根据用户不同缓存是怎么做,或者其它的不同?在中间件中取唯一码,生成不同的缓存 key?
cyhone
2021-06-18 16:30:22 +08:00
@meshell 好问题。gin-cache 提供了两个快捷函数,CacheByPath 和 CacheByUri,如果用户的信息(比如 uid)就在 url 中,直接使用这两个函数就行。

如果需要从其他地方获取用户的唯一信息(例如 header 、body ),gin-cache 也支持用户自定义 KeyGenerator,这样可以自行写函数,根据不同请求,生成不同的 cache key
xkeyideal
2021-06-18 16:40:40 +08:00
inmemory cache 以为是自己写的,没想到居然用的是”大名鼎鼎“的 https://github.com/patrickmn/go-cache fork 而来的库,在此呢,想劝你改掉,要么自己写一个,要么换个别的,换之前呢,类似 cache 这种代码量没多少,先阅读一下源码,看看有没有 bug 或性能问题。

看到此贴的人,在 inmemory 没有换底层库之前,不要使用此缓存中间件,否则线上 P0 随时等着你
meshell
2021-06-18 16:41:09 +08:00
@cyhone 针对当前用户标识,一般我们都在 logic server 里面拿 uid 。还有就是我是不是要告诉中间件,我这个请求需要缓存,需要根据啥规则缓存,时间等。这些是直接写在 cache middleware 里面?
xkeyideal
2021-06-18 16:45:40 +08:00
@meshell 再修复问题之前,先把在各个论坛上推广的帖子给屏蔽一下吧,inmemory 不能在较大数据量场景下使用,这是在给使用者制造故障,会把别人饭碗砸掉的
xkeyideal
2021-06-18 16:46:17 +08:00
@cyhone @错人了 [😅]
cyhone
2021-06-18 16:54:04 +08:00
@xkeyideal 感谢反馈此库的问题。但需要说明的两点是:
1. 并不是 fork 了此库。而是使用了该库作为内置的 inmemory 实现,是属于依赖而非 fork 关系。
2. 如果觉得内置方案有问题,是可以替换为其他的自己的方案的。只要实现 persist.CacheStore 就可以

此外,感谢反馈,我先研究下这个库的问题。同时已将该贴下沉了一天~
cyhone
2021-06-18 17:20:09 +08:00
@meshell 是的,这些信息需要提供给 cache middleware 。但目前自定义 cache 策略中,暂时只支持生成 cache key, 以及是否要进行 cache 。缓存时间目前沿用了 api 默认的缓存时间,暂不支持每个请求自定义,不过这个 case 感觉可以加一下~
xkeyideal
2021-06-18 17:23:01 +08:00
@cyhone 有问题反馈给作者是应该的,面向接口编程是好的设计,提供的实现方案也不应该有严重的潜在问题,这个库的问题之前有位线上出故障了,最终找到是此库的原因,还拿出来秀了一遍故障复盘。

如果找不到问题可以找我

inmemory cache 的实现无非需注意以下几点:
1 、考虑到大数据量时的查询性能与并发安全,直接解决方案就是分片存储与加锁
2 、此类 cache 一般都会涉及过期自动删除问题,如何最少化遍历或尽可能少的遍历,解决方案主流就是空间换时间,链表或堆
3 、结合实际项目需求做相应的需求封装,建议不要使用或少使用第三方开源的 cache (因为并不难写)

再提供一个可能存在问题的 cache 库,`muesli/cache2go` 三年前有位同事使用该库出现过 bug,原因好像是死锁,如今有无修复不清楚。
cyhone
2021-06-18 17:44:18 +08:00
@xkeyideal 明白了~ 我研究下这里,之后应该会一个更好的方案替代这个库。
2liuqi
2022-05-20 00:17:58 +08:00
@xkeyideal 那只能我们重新造轮子了吗?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/784290

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX