Gorm配置限流中间件

前言:

本文介绍如何对在Golang中使用自定义Gorm中间件并制作一个限流中间件。

问题引入

MySQL在大量线程并发工作时遇到线程调度工作过多、大量缓存失效、资源竞争加剧、锁冲突严重的情况下,可能导致系统资源耗尽,进而导致整个数据库服务不可用。

解决方案:对MySQL进行限流,保证在超高并发场景下仍能保证正常服务。

引入限流

这里我们使用Golang官方实现好的RateLimiter编写中间件。

1
import "golang.org/x/time/rate"

1.编写中间函数

  • 方案1:Gorm 默认提供了Plugin接口,实现这个接口就可以在初始化的时候自动把中间件注册进去。

  • 方案2:使用Gorm提供的Hook函数,实现自定义中间件。

这里我采用了方案2对限流进行实现,因为这个接口实现我暂时没搞懂设计的核心思想,也没有看到很好的示例。

2. 实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

var (
GormToManyRequestError = errors.New("gorm: to many request")
)


// Init(dsn string) Gorm初始化代码
func InitDataBase(dsn string) {
dbMake, err := gorm.Open(mysql.New(mysql.Config{
DSN: dsn, // DSN data source name
DefaultStringSize: 256, // string 类型字段的默认长度
DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
SkipInitializeWithVersion: false, // 根据版本自动配置
}), &gorm.Config{
Logger: ormLogger,
NamingStrategy: schema.NamingStrategy{
SingularTable: true,
},
})

sqlDB, _ := dbMake.DB()
sqlDB.SetMaxIdleConns(20) //设置连接池,空闲
sqlDB.SetMaxOpenConns(100) //打开
sqlDB.SetConnMaxLifetime(time.Second * 30)

// 中间件引入
GormRateLimiter(dbMake, rate.NewLimiter(500, 1000)) // 限流器参数R:代表生产令牌速率,B:代表桶的容量,本示例 每秒生产500个令牌,最大存放数量1000个。具体参数调整根据你的数据库配置了。
}

// GormRateLimiter Gorm限流器
func GormRateLimiter(db *gorm.DB, r *rate.Limiter) {
// 对所有的查询语句执行之前注册绑定令牌桶限流回调函数。
err := db.Callback().Query().Before("*").Register("RateLimitGormMiddleware", func(d *gorm.DB) {
// 桶没有令牌,那么直接为本次查询添加错误信息,
if !r.Allow() {
d.AddError(GormToManyRequestError)
global.Log.Error(GormToManyRequestError.Error())
return
}
})
if err != nil {
panic(err)
}
}

3.总结

如果业务中有遇到数据库在高并发场景下被打爆崩溃的情况,那么可以分析一下数据库的最大负载能力并使用限流中间件对MySQl进行过载保护,防止服务不可用。

以上是我对GormHook的浅薄认知,相信还有有更多的功能等待发掘。

知识点:

  1. 认识了官方库实现的 RateLimiter 组件 通过 rate.NewLimiter(r,h) 就可以初始化一个限流桶。r.Allow() 每次消费一个令牌,并判断桶中令牌是否大于1.
  2. 认识了Gorm Hook,通过GormHook我们可以事前规定需要捕获的映射语句,并对其注册相应的业务回调函数,这样当我们执行的时候就能够触发回调函数啦。

Gorm配置限流中间件
http://yoursite.com/2023/07/30/Gorm配置限流中间件/
作者
Meng-Xin
发布于
2023年7月30日
许可协议