不管你的服务器有多少 CPU、多大量的记忆体,每秒可以处理的请求数终究是有限的。为了避免资源被少数恶意使用者用完,造成阻断服务攻击(Denial Of Service),一定要限制每个使用者能够使用的资源量。
而最常见的资源限制方式,就是做限流(rate limiting),当一个使用者无法再发请求给服务器时,自然就没办法再消耗服务器上的资源
如果你架设的 API 服务除了用 Node.js/Go 写成的 application server 之外,前面没有挡任何 reverse proxy、load balancer,那最简单的方式就是用 express/gin 的 middleware 来做限流了
在 Node.js 里,有一个用来做限流的 express middleware 叫做 express-rate-limit,如果我想限制每个 IP 在十分钟内最多只能发 1000 个请求到 server,那只要这样写就可以了
const rateLimit = require("express-rate-limit");
const globalLimiter = rateLimit({
windowMs: 10 * 60 * 1000, // 10 分钟
max: 1000
});
app.use(globalLimiter)
app.get('/api/user', getUserHandler)
app.post('/api/user', createUserHandler)
// ...
如此一来,只要有人尝试在十分钟内发送超过 1000 个请求给 API Server,就会马上收到 429 Too Many Requests
而 Go 的话也有一个 Gin 的 middleware 叫做 limiter,用起来稍微复杂一点,但基本概念就是设定好时间跟次数後,把 middleware 套用上去就可以了
package main
import (
"github.com/ulule/limiter/v3"
"github.com/ulule/limiter/v3/drivers/store/memory"
mgin "github.com/ulule/limiter/v3/drivers/middleware/gin"
"github.com/gin-gonic/gin"
)
func main() {
// 每十分钟最多 1000 个请求
rate := limiter.Rate{
Period: 10 * time.Minute,
Limit: 1000,
}
// 把资料存在记忆体里面
store := memory.NewStore()
middleware := mgin.NewMiddleware(limiter.New(store, rate))
router := gin.Default()
// 套用限流的 middleware
router.Use(middleware)
router.GET("/api/user", getUserHandler)
router.POST("/api/user", createUserHandler)
router.Run(":8888")
}
一般来说根据每个 API endpoint 要做的事情不同,所需要的资源(CPU、时间、$$$)也会不同,所以像上面的范例只有规定「每十分钟内最多一千个请求」并不是个好方法,应该要针对高成本的 endpoint 做更严格的限制
举个比较极端的例子,譬如说你有一个 API endpoint POST /api/verify/{phoneNumber}
是用来发送认证简讯的,而每发一封简讯需要 0.5 元。所以如果攻击者在十分钟内连续送出 1000 个发送简讯的请求,就算你的 server 顶得住流量,也会让你每十分钟就浪费 500 元(一天就喷 72000),可能公司还没开始赚钱就倒了XD
所以为了防止这种情况,应该要针对特别浪费资源(不管是运算资源还是 $$$)的 api 做特别的限制,才不会被攻击者找到弱点後直接被打挂
const rateLimit = require("express-rate-limit");
const globalLimiter = rateLimit({
windowMs: 10 * 60 * 1000,
max: 1000
});
// 每个小时最多发 5 次简讯
const verifyPhoneLimiter = rateLimit({
windowMs: 60 * 60 * 1000,
max: 5
});
app.use(globalLimiter)
app.get('/api/user', getUserHandler)
app.post('/api/verify/:phoneNumber', verifyPhoneLimiter, verifyPhoneHandler)
今天讲了怎麽在 API server 里面自己用 middleware 做限流,虽然这样做感觉很方便,但也会让 API server 无法完全专注在业务逻辑上,所以明天要来说说如果在 API server 前面有挡 nginx 之类的 load balancer 的话,要怎麽在上面设定限流以减低 API server 的负担
如果对於今天的内容有什麽问题的话欢迎在下方留言跳出来,没有的话那我们就明天见罗~
<<: 使用MLFlow tracking功能比较training结果
嗨!来到了第19天~今天要谈的是Sass界的计算机 @function与@return 在规模比较大...
再写登入的验证及功能 今天我们要来做登入的判断跟动作, 我们在HomeController.php引...
注:本文同步刊载在Medium,若习惯Medium的话亦可去那边看呦! 接下来让我们来聊聊Pytho...
针对 dropdown 的部分,我们要来细节微调他的 style ,让他符合 vogue 上的设计,...
Fluent bit回顾 Log Agent - Fluent Bit 简介 Log Agent -...