V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
cyhone
V2EX  ›  Go 编程语言

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

  •  
  •   cyhone · 2021-06-18 16:17:05 +08:00 · 2068 次点击
    这是一个创建于 1283 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

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

    cache

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

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

    点击查看原文>>

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

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

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

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

    如果找不到问题可以找我

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

    再提供一个可能存在问题的 cache 库,`muesli/cache2go` 三年前有位同事使用该库出现过 bug,原因好像是死锁,如今有无修复不清楚。
    cyhone
        10
    cyhone  
    OP
       2021-06-18 17:44:18 +08:00
    @xkeyideal 明白了~ 我研究下这里,之后应该会一个更好的方案替代这个库。
    2liuqi
        11
    2liuqi  
       2022-05-20 00:17:58 +08:00
    @xkeyideal 那只能我们重新造轮子了吗?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2733 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 15:00 · PVG 23:00 · LAX 07:00 · JFK 10:00
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.