首页  >  问答  >  正文

仅在生产环境中使用Redis进行限流的Laravel实践

背景

默认情况下,Laravel提供了两个中间件用于速率限制(节流):

\Illuminate\Routing\Middleware\ThrottleRequests::class
\Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class

正如文档所述,如果您使用Redis作为缓存驱动程序,您可以在Kernel.php中更改映射,如下所示:

/**
 * 应用程序的中间件别名。
 *
 * 别名可用于将中间件方便地分配给路由和组,而不是使用类名。
 *
 * @var array<string, class-string|string>
 */
protected $middlewareAliases = [
    // ...
    'throttle' => \Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class
    // ...
];

问题

问题在于上述内容不是动态的,而是依赖于环境。例如,在我的stagingproduction环境中,我使用Redis,但在我的localdevelopment环境中,我不使用Redis。

可能的解决方案

有一个明显的脏解决方案,类似于这样(Kernel.php):

/**
 * 应用程序的中间件别名。
 *
 * 别名可用于将中间件方便地分配给路由和组,而不是使用类名。
 *
 * @var array<string, class-string|string>
 */
protected $middlewareAliases = [
    // ...
    'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class
    // ...
];

/**
 * 创建一个新的HTTP内核实例。
 *
 * @param  \Illuminate\Contracts\Foundation\Application  $app
 * @param  \Illuminate\Routing\Router  $router
 * @return void
 */
public function __construct(Application $app, Router $router)
{
    if ($app->environment('production')) {
        $this->middlewareAliases['throttle'] = \Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class;
    }

    parent::__construct($app, $router);
}

是否有一种“标准”方法可以在不重写Kernel构造函数的情况下实现这一点?基本上,我希望我的应用程序可以根据环境是否设置为production(或者默认缓存存储设置为redis)动态选择相关的中间件。

更新

上述解决方案不起作用,因为在引导应用程序之前访问内核,因此此时环境不可用。我现在正在研究的另一种解决方案是扩展基本的ThrottleRequests类,以便动态调用相关的类。

P粉541565322P粉541565322257 天前357

全部回复(1)我来回复

  • P粉043432210

    P粉0434322102024-01-11 16:27:49

    经过大量的研究和测试,我得出的结论是在RouteServiceProvider中动态设置throttle中间件是最好的解决方案,代码如下:

    class RouteServiceProvider extends ServiceProvider
    {
        /**
         * 启动任何应用程序服务。
         *
         * @return void
         */
        public function boot(): void
        {
            $this->registerThrottleMiddleware();
        }
    
        /**
         * 注册用于限制请求的中间件。
         *
         * @return void
         */
        private function registerThrottleMiddleware(): void
        {
            $router = app()->make('router');
    
            $middleware = config('cache.default') !== 'redis'
                ? \Illuminate\Routing\Middleware\ThrottleRequests::class
                : \Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class;
    
            $router->aliasMiddleware('throttle', $middleware);
        }
    }

    回复
    0
  • 取消回复