Description
项目地址
https://github.com/mgtv-tech/jetcache-go
类别
Go
项目标题
🚀 JetCache-Go - 媲美阿里 JetCache 的Go版本多级缓存框架
项目描述
一、背景
背景是用 golang 开发一个并发量要求比较高的项目。提到高并发系统设计,就离不开三大利器:缓存、限流、降级。然而,在 golang 的开源世界,居然没找到一个趁手的工具:
要支持:
- 多级缓存
- 批量查询
- 泛型
- 自动刷新缓存
- 缓存一致性机制
- 指标采集
- 自动降级
要解决:
- 缓存穿透
- 缓存击穿
- 大 Key
- 热 Key
- 雪崩
要好用:
- 透明代理
- 灵活拓展
因此,就参考阿里巴巴开源的 JetCache 核心设计,打造了 Go 版本的 jetcache-go。
亮点
jetcache-go是基于go-redis/cache拓展的通用缓存访问框架。 实现了类似Java版JetCache的核心功能,包括:
✅ 二级缓存自由组合:本地缓存、分布式缓存、本地缓存+分布式缓存
✅ Once接口采用单飞(singleflight)模式,高并发且线程安全
✅ 默认采用MsgPack来编解码Value。可选sonic、原生json
✅ 本地缓存默认实现了Ristretto和FreeCache
✅ 分布式缓存默认实现了go-redis/v9的适配器,你也可以自定义实现
✅ 可以自定义errNotFound,通过占位符替换,缓存空结果防止缓存穿透
✅ 支持开启分布式缓存异步刷新
✅ 指标采集,默认实现了通过日志打印各级缓存的统计指标(QPM、Hit、Miss、Query、QueryFail)
✅ 分布式缓存查询故障自动降级
✅ MGet接口支持Load函数。带分布缓存场景,采用Pipeline模式实现 (v1.1.0+)
✅ 支持拓展缓存更新后所有GO进程的本地缓存失效 (v1.1.1+)
示例代码
截图或演示视频
使用示例:
例举了两种常用的接口使用方式
- Once 接口查询,并开启缓存自动刷新
- MGet(稀疏列表缓存)泛型接口查询
我们内部脚手架封装好了通过配置即可初始化缓存实例。红线部分就是被代理的接口,哪怕是遇到了类似
gorm.RecordNotFound
也能被缓存并透明代理,有没有感觉使用非常简单?
功能概览:
缓存指标统计:
支持实现stats.Handler接口并注册到 Cache 组件来自定义收集指标,例如使用Prometheus 采集指标。
我们默认实现了通过日志打印统计指标,如下所示:
2023/09/11 16:42:30.695294 statslogger.go:178: [INFO] jetcache-go stats last 1m0s.
cache | qpm| hit_ratio| hit| miss| query| query_fail
------------+------------+------------+------------+------------+------------+------------
bench | 216440123| 100.00%| 216439867| 256| 256| 0|
bench_local | 216440123| 100.00%| 216434970| 5153| -| -|
bench_remote| 5153| 95.03%| 4897| 256| -| -|
------------+------------+------------+------------+------------+------------+------------
实现stats.Handler接口并将指标采集到 Prometheus
配置项说明:
可以看到,我们内置了很多例如 分布式缓存、本地缓存、序列化方式、命中率统计、日志等多种接口实现,用户也可以自己去实现相关接口做拓展。
配置项名称 | 配置项类型 | 缺省值 | 说明 |
---|---|---|---|
name | string | default | 缓存名称,用于日志标识和指标报告 |
remote | remote.Remote 接口 |
nil | remote 是分布式缓存,例如 Redis。也可以自定义,实现remote.Remote 接口即可 |
local | local.Local 接口 |
nil | local 是内存缓存,例如 FreeCache、TinyLFU。也可以自定义,实现local.Local 接口即可 |
codec | string | msgpack | value的编码和解码方法。默认为 "msgpack"。可选:json | msgpack | sonic ,也可以自定义,实现encoding.Codec 接口并注册即可 |
errNotFound | error | nil | 回源记录不存在时返回的错误,例:gorm.ErrRecordNotFound 。用于防止缓存穿透(即缓存空对象) |
remoteExpiry | time.Duration |
1小时 | 远程缓存 TTL,默认为 1 小时 |
notFoundExpiry | time.Duration |
1分钟 | 缓存未命中时占位符缓存的过期时间。默认为 1 分钟 |
offset | time.Duration |
(0,10]秒 | 缓存未命中时的过期时间抖动因子 |
refreshDuration | time.Duration |
0 | 异步缓存刷新的间隔。默认为 0(禁用刷新) |
stopRefreshAfterLastAccess | time.Duration |
refreshDuration + 1秒 | 缓存停止刷新之前的持续时间(上次访问后) |
refreshConcurrency | int | 4 | 刷新缓存任务池的并发刷新的最大数量 |
statsDisabled | bool | false | 禁用缓存统计的标志 |
statsHandler | stats.Handler 接口 |
stats.NewStatsLogger | 指标统计收集器。默认内嵌实现了log 统计,也可以使用jetcache-go-plugin 的Prometheus 插件。或自定义实现,只要实现stats.Handler 接口即可 |
sourceID | string | 16位随机字符串 | 【缓存事件广播】缓存实例的唯一标识符 |
syncLocal | bool | false | 【缓存事件广播】启用同步本地缓存的事件(仅适用于 "Both" 缓存类型) |
eventChBufSize | int | 100 | 【缓存事件广播】事件通道的缓冲区大小(默认为 100) |
eventHandler | func(event *Event) |
nil | 【缓存事件广播】处理本地缓存失效事件的函数 |
separatorDisabled | bool | false | 禁用缓存键的分隔符。默认为false。如果为true,则缓存键不会使用分隔符。目前主要用于泛型接口的缓存key和ID拼接 |
separator | string | : | 缓存键的分隔符。默认为 ":"。目前主要用于泛型接口的缓存key和ID拼接 |