前言:
本文介绍如何对在Golang中使用自定义Gorm中间件并制作一个限流中间件。
问题引入
MySQL在大量线程并发工作时遇到线程调度工作过多、大量缓存失效、资源竞争加剧、锁冲突严重的情况下,可能导致系统资源耗尽,进而导致整个数据库服务不可用。
解决方案:对MySQL进行限流,保证在超高并发场景下仍能保证正常服务。
引入限流
这里我们使用Golang官方实现好的RateLimiter编写中间件。
1
| import "golang.org/x/time/rate"
|
1.编写中间函数
这里我采用了方案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") )
func InitDataBase(dsn string) { dbMake, err := gorm.Open(mysql.New(mysql.Config{ DSN: dsn, DefaultStringSize: 256, DisableDatetimePrecision: true, DontSupportRenameIndex: true, DontSupportRenameColumn: true, 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)) }
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的浅薄认知,相信还有有更多的功能等待发掘。
知识点:
- 认识了官方库实现的
RateLimiter
组件 通过 rate.NewLimiter(r,h) 就可以初始化一个限流桶。r.Allow() 每次消费一个令牌,并判断桶中令牌是否大于1.
- 认识了Gorm Hook,通过GormHook我们可以事前规定需要捕获的映射语句,并对其注册相应的业务回调函数,这样当我们执行的时候就能够触发回调函数啦。