Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >workerman基于Lua脚本Redis限流令牌桶中间件实例

workerman基于Lua脚本Redis限流令牌桶中间件实例

原创
作者头像
OwenZhang
发布于 2022-05-30 07:10:58
发布于 2022-05-30 07:10:58
5100
举报
文章被收录于专栏:Owen's WorldOwen's World

本文环境 CentOS8.0,PHP8.1,Nginx1.8,Workerman 4.0\ 不懂的可以评论联系我。 著作权归OwenZhang所有。商业转载请联系OwenZhang获得授权,非商业转载请注明出处。

简要

为防止滥用,你应该考虑对您的 API 限流。 例如,您可以限制每个用户 10 分钟内最多调用 API 100 次。 如果在规定的时间内接收了一个用户大量的请求,将返回响应状态代码 429 (这意味着过多的请求)。

workerman介绍

Workerman是一款纯PHP开发的开源高性能的PHP 应用容器

Workerman不是重复造轮子,它不是一个MVC框架,而是一个更底层更通用的服务框架,你可以用它开发tcp代理、梯子代理、做游戏服务器、邮件服务器、ftp服务器、甚至开发一个php版本的redis、php版本的数据库、php版本的nginx、php版本的php-fpm等等。Workerman可以说是PHP领域的一次创新,让开发者彻底摆脱了PHP只能做WEB的束缚。

实际上Workerman类似一个PHP版本的nginx,核心也是多进程+Epoll+非阻塞IO。Workerman每个进程能维持上万并发连接。由于本身常驻内存,不依赖Apache、nginx、php-fpm这些容器,拥有超高的性能。同时支持TCP、UDP、UNIXSOCKET,支持长连接,支持Websocket、HTTP、WSS、HTTPS等通讯协议以及各种自定义协议。拥有定时器、异步socket客户端、异步Redis、异步Http、异步消息队列等众多高性能组件。

在原作者的基础上做的修改

原地址:

https://www.workerman.net/plugin/37

https://github.com/Tinywan/webman-limit-traffic

方便不需要安装composer扩展,哈哈,但还是支持原作者!

代码实例

config/middleware.php

代码语言:txt
AI代码解释
复制
\app\middleware\LimitTraffic::class,//令牌桶限流

app/middleware/LimitTraffic.php

代码语言:txt
AI代码解释
复制
<?php
/**
 * Programmer: Owen Zhang
 * Email: owen@owenzhang.com
 * Start Date: 05/15/22
 * Last Update: 05 15, 2022 [OZ]
 * Functions:
 * 基于 Lua 脚本简单限流令牌桶,应用接口服务限流工具
 * 在原作者的基础上做的修改
 * 原地址:
 *      https://www.workerman.net/plugin/37
 *      https://github.com/Tinywan/webman-limit-traffic
 */

namespace app\middleware;

use app\common\ApiStatus;
use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;
use support\Redis;

class LimitTraffic implements MiddlewareInterface
{
    public const LIMIT_TRAFFIC_SCRIPT_SHA = 'limit:traffic:script';
    public const LIMIT_TRAFFIC_PRE        = 'limit:traffic:pre:';

    public function process(Request $request, callable $next): Response
    {
        if ($result = $this->traffic()) {
            return new Response($result['status'], [
                'Content-Type' => 'application/json',
            ], json_encode($result['body']));
        }
        return $next($request);
    }

    /**
     * 校测
     *
     * @return array|false
     */
    public function traffic()
    {
        $config    = $this->getRateLimit();
        $scriptSha = Redis::get(self::LIMIT_TRAFFIC_SCRIPT_SHA);
        if (!$scriptSha) {
            $script    = <<<luascript
            local result = redis.call('SETNX', KEYS[1], 1);
            if result == 1 then
                return redis.call('expire', KEYS[1], ARGV[2])
            else
                if tonumber(redis.call("GET", KEYS[1])) >= tonumber(ARGV[1]) then
                    return 0
                else
                    return redis.call("INCR", KEYS[1])
                end
            end
luascript;
            $scriptSha = Redis::script('load', $script);
            Redis::set(self::LIMIT_TRAFFIC_SCRIPT_SHA, $scriptSha);
        }
        $limitKey = self::LIMIT_TRAFFIC_PRE . request()->getRealIp(true);
        $result   = Redis::rawCommand('evalsha', $scriptSha, 1, $limitKey, $config['limit'], $config['window_time']);
        if ($result === 0) {
            return [
                'limit'     => $config['limit'],
                'remaining' => $config['limit'] - Redis::get($limitKey),
                'reset'     => Redis::ttl($limitKey),
                'status'    => $config['status'],
                'body'      => $config['body'],
            ];
        }
        return false;
    }

    /**
     * @desc   : 返回允许的请求的最大数目及时间,例如,[100, 600] 表示在 600 秒内最多 100 次的 API 调
     * @author Tinywan(ShaoBo Wan)
     */
    public function getRateLimit(): array
    {
        return [
            'limit'       => 100, // 请求次数
            'window_time' => 5, // 窗口时间,单位:秒
            'status'      => 429,  // HTTP 状态码
            'body'        => [
                // 响应返回的结构信息
                'code' => ApiStatus::API_ERROR->value,
                'msg'  => 'Too Many Requests'
            ],
        ];
    }
}

运行状态

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
💥开发者 MCP广场重磅上线!
精选全网热门MCP server,让你的AI更好用 🚀
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档