gin的Abort()方法使用
源代码/数据集已上传到 Github - posts
gin的Abort()
方法使用
食用本篇文章,请先看完Gin的Next()方法使用
简单的Http服务的实现
先来实现一个简单的http服务
1 | package main |
我们在程序中引入了4个中间件,来实现非嵌入式的业务处理。
简化后的中间件代码如下(demo代码中,4个中间件只有数字的差异)
1 | package md1 |
运行起来后,请求,可以在日志中看到如下输出:
1 | [GIN-debug] GET / --> main.main.func1 (6 handlers) |
依旧是非常典型的洋葱形式的输出。
使用Abort()
的效果
md1 是风控模块
假设中间1是一个风控模块,当检测到请求参数异常时,立即进行拦截,不再继续执行后续操作。
我们可以在A中加入Abort()
,让他跳过后续步骤的执行。中间件1的实现代码
1 | func Md1(ctx *gin.Context) { |
此时我们通过风控链接访问到服务,就会触发风控机制,直接被abort。
1 | [GIN-debug] GET / --> main.main.func1 (6 handlers) |
md2是登录验证模块
假设中间2是一个登录验证模块,未登录的用户会在这一层被拦截
1 | func Md2(ctx *gin.Context) { |
当一个未登录的用户访问过来,就会被这一层拦截,不再执行后续的代码
1 | [GIN-debug] GET / --> main.main.func1 (6 handlers) |
Abort()
如何实现它的功能
从上一篇Next
的文章中我们可以知道中间件的执行过程是 Md1.start
-> Md2.start
-> Md3.start
-> service
-> Md3.end
-> Md2.end
-> Md1.end
。当中间件中遇到异常执行了abort时,他会中止执行后续的中间件的逻辑,直接结束掉。流程就是Md1.start
-> Md2.start
-> Md2.end
-> Md1.end
。
那Abort()
是如何实现他的这个功能的呢?查看代码后会发现,它的实现非常简单,只有一行代码。
1 | c.index = abortIndex |
它的作用就是把中间件的索引位放置到第64位,直接跳过前置的中间件的运行。 (const abortIndex = (1<<7 - 1) / 2 )
与Abort()
方法对应的IsAborted()
就是通过当前索引位是否大于等于63。
更优雅地使用Abort()
从上文日志中可以发现,当使用Abort()
时,不会有任何的输出。而一个在线服务就算是报错了也需要一个内容返回。那如何更优雅地给出提示?我们可以调用AbortWithStatusJSON(code int, jsonObj interface{})
来输出json内容。如果不满足输出可以,可以自己封装一个Abort()
。
中间件+Abort()
组合有何作用
- 风控拦截,在中间件层实现风控拦截,可以避免业务代码与风控代码耦合度过高
- 登录验证,在中间件层做完权鉴,通过上下文传递,实现业务代码与权鉴的解耦
- 黑名单拦截,在中间件层实现黑名单拦截,可以使业务代码可以更加专注于实现业务
- 限流,在中间件层处理限流逻辑,可以提升系统的横向扩展能力
潜在的问题
常规情况下一个系统不会有超过64个中间件。一旦中间件超过64个,使用isAbort()
就会出现不在预期的结果。
引用
【1】文章相关代码
微信扫一扫,阅读/分享
edit this page last updated at 2024-04-22