PHP 如何做接口限流

在日常的网站或APP使用中,你有没有遇到过这样的情况:点了几下刷新,页面突然弹出“访问太频繁,请稍后再试”的提示?这不是系统出了故障,而是后台的“接口限流”在发挥作用——就像商场为了避免人太多发生拥挤,会控制同一时间进入的顾客数量,接口限流就是控制同一时间访问某个功能的请求数量,防止服务器被挤垮。

最常用的:令牌桶限流法

令牌桶是个很形象的例子,你可以把它想象成一个装着“访问令牌”的小桶。系统会按固定速度往桶里加令牌,比如每秒加5个,桶最多能装10个。用户要访问接口,得先从桶里拿一个令牌,拿到了才能访问,拿不到就只能等下一批令牌。

PHP 如何做接口限流

举个生活化的例子:小区门口的快递柜,同一时间最多有10个空柜子(对应桶的最大容量),快递员每秒能放5个包裹(对应加令牌的速度)。如果一下子来了15个快递员,前10个能直接放,后5个只能等1秒,等快递员取走一个包裹(对应令牌被用掉),空出柜子,下一秒再放新的。用PHP实现这个逻辑也很简单,比如用Redis来存令牌数量,每次请求先检查Redis里的令牌数,不够就返回错误,够就减1,同时按时间间隔补加令牌。

最简单的:固定窗口限流法

固定窗口就是按固定时间分段来算请求数,比如每10秒为一个窗口,每个窗口最多允许100次请求。超过这个数,窗口内剩下的请求就被拒绝。

这个方法好比小区的门禁,每10秒只能进100个人,10秒到了就重新计数。比如前9秒已经进了100人,第10秒再来人也不让进,直到第11秒才能再放人。它的优点是实现起来特别简单,不用复杂的计算,只要把请求次数存在缓存里,每次请求判断次数有没有超就行。不过它有个小缺点,比如两个窗口的边界,前一个窗口最后1秒进了90人,后一个窗口第一秒又进了90人,这2秒内一共来了180人,远超过100的限制,可能会让服务器有点扛不住。

更精准的:滑动窗口限流法

为了弥补固定窗口的漏洞,就有了滑动窗口——把时间窗口切成更小的“小格子”,比如把10秒分成10个1秒的小格子,每个格子记录这1秒的请求数,然后计算最近10秒的总请求数,超了就限流。

还是用门禁的例子,把10秒分成10个1秒的时间段,每个段记人数。比如第9秒到第10秒有90人,第10秒到第11秒有90人,算最近10秒的话,要去掉第0到1秒的人数,加第10到11秒的人数,总人数就不会一下子到180。用PHP实现的话,就是把每个时间点的请求时间戳存在Redis的列表里,每次请求先把旧的时间戳删掉,再统计列表里的数量,判断是否超过限制。

PHP实现的小细节

不管用哪种方法,PHP做接口限流都离不开缓存工具,比如Redis或者Memcached,因为这些工具能快速读写数据,比数据库快很多,不会拖慢接口速度。比如用Redis的incrby命令来统计请求数,用expire命令设置过期时间,就能轻松实现固定窗口限流。

另外,限流的规则不是固定的,比如注册接口、支付接口可以严一点,每秒只允许10次请求;而商品浏览接口可以松一点,每秒允许100次。所以在PHP代码里,最好把限流规则写成配置项,方便后期修改,不用改代码。

接口限流不是为了不让用户用,而是为了让更多用户能稳定用。就像餐厅控制每桌的用餐时间,是为了让后面的客人也能吃上饭,而不是把所有人拒之门外。合理的限流,既能保护服务器,又能让用户的使用体验更稳定。

上一篇 C / C++ 入门必知核心语法与高效编程技巧科普指南