You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Twig缓存耗时10秒陷入循环致Slim站点崩溃,求原因及缓存建议

问题解答

1. 这种情况是否正常?

完全不正常。Twig启用缓存后,正常逻辑是仅在模板文件修改时重新生成缓存,50个活跃用户就触发重复生成缓存并导致站点崩溃,明显是缓存机制出现了异常。

2. 可能的原因分析

结合你提供的代码和日志,几个大概率的问题点:

  • Twig缓存目录权限问题:PHP-FPM进程没有对API_DIR . '/var/cache'目录的读写权限,导致缓存文件无法正常写入,每次请求都尝试重新生成缓存,最终引发进程阻塞。
  • Twig缓存与PHP-DI缓存共用目录:你的代码中同时启用了PHP-DI的容器编译缓存和Twig视图缓存,两者共用/var/cache目录,可能存在文件冲突、锁竞争的情况,导致缓存生成逻辑异常。
  • 模板文件路径动态变化:如果你的模板加载逻辑(比如$loader->addPath)在每次请求时都动态添加路径,或者模板文件的真实路径存在不稳定因素(比如软链接变化、挂载点波动),Twig会认为模板已修改,从而重复生成缓存。
  • Twig debug模式在生产环境未关闭:你在生产环境下Twig的debug参数仍设为true,debug模式会额外检查模板文件的修改时间,可能增加文件系统IO压力,甚至在某些场景下触发不必要的缓存重建。
  • 请求路由的动态解析逻辑:你的404处理路由中,通过$request->getAttribute('data')动态决定控制器和方法,如果这个data属性的获取逻辑存在异常,导致大量请求进入错误分支(比如频繁触发404),而404模板的缓存生成又存在问题,会放大异常影响。

3. 改用Nginx服务器缓存是否更优?

这是一个可行的优化方向,但需要分场景看待:

  • 优势:
    • Nginx缓存是在服务器层面直接返回静态内容,完全绕过PHP-FPM,性能比Twig视图缓存高出几个量级,能极大降低后端压力。
    • 避免PHP层面的缓存锁竞争、权限等问题,稳定性更高。
  • 局限性:
    • 仅适用于静态或准静态页面(比如内容很少更新的文章、分类页);如果页面包含大量用户个性化内容(比如登录用户的专属信息),Nginx缓存无法直接处理,需要结合Vary头或分段缓存。
    • 缓存失效策略需要额外处理(比如内容更新时手动清理Nginx缓存,或设置合理的过期时间),否则用户会看到旧内容。
  • 建议:
    • 优先修复Twig缓存的问题(比如单独给Twig分配缓存目录、修正权限、关闭生产环境debug模式),作为基础优化。
    • 在此基础上,对适合静态缓存的页面添加Nginx层缓存,实现多级缓存的架构,进一步提升性能和稳定性。

附:相关代码及日志信息

日志截图

日志截图

404处理路由代码

$app->group('', function(RouteCollectorProxy $group){
    $group->get('/{slug:.*}', function($request, $response, array $args) {
        $res = $request->getAttribute('data');
        if(isset($res)) {
            if($res['type'] == 'course') {
                $controllerClass = '\App\Controllers\Frontend\ControllerCommon';
                $method = 'viewPlayer';
            }
            else if($res['type'] == 'category') {
                $controllerClass = '\App\Controllers\Frontend\Controller'.$res['code'];
                if($res['parent_id'] == 0)
                    $method = 'viewParentCategory';
                else
                    $method = 'viewSingleCategory';
            }
            else {
                $controllerClass = '\App\Controllers\Frontend\Controller'.$res['code'];
                if(!empty($res['template'])){
                    $method = $res['template'];
                }
                else {
                    if($res['parent_id'] == 0)
                        $method = 'viewParentPage';
                    else
                        $method = 'viewSinglePage';
                }
            }
        }
        else {
            $controllerClass = '\App\Controllers\Frontend\ControllerCommon';
            $method = 'notFound';
        }

        $controller = new $controllerClass($this);
        return call_user_func_array([$controller, $method], func_get_args());
    });

Index.php核心代码

// Instantiate PHP-DI ContainerBuilder
$containerBuilder = new ContainerBuilder();

if (PRODUCTION) { // container compiled and cached
    $containerBuilder->enableCompilation(API_DIR . '/var/cache');
}

// Set up settings
$settings = require API_DIR . '/libs/settings.php';
$settings($containerBuilder);

// Set up dependencies
$dependencies = require API_DIR . '/libs/dependencies.php';
$dependencies($containerBuilder);

// Set up helper
$helper = require API_DIR . '/libs/Helper.php';

// Build PHP-DI Container instance
$container = $containerBuilder->build();

// Instantiate the app
AppFactory::setContainer($container);
$app = AppFactory::create();
$app->setBasePath(MAIN_DIR);
$callableResolver = $app->getCallableResolver();

// Create Twig
$twig = Twig::create(TEMPLATE_PATH, ['cache' => (PRODUCTION) ? API_DIR . '/var/cache' : false, 'debug' => true]);

// Add Assets Path
$loader = $twig->getLoader();
if ($loader instanceof FilesystemLoader) {
    $loader->addPath($container->get('settings')['assets']['path'], 'public');
}

// Add Asset Extensions
$environment = $twig->getEnvironment();
$twig->addExtension(new TwigAssetsExtension($environment, (array)$container->get('settings')['assets']));

// Add Twig-View Middleware
$app->add(TwigMiddleware::create($app, $twig));

// Register routes
// if (PRODUCTION) { // routes compiled and cached
//     $routeCollector = $app->getRouteCollector();
//     $routeCollector->setCacheFile(API_DIR . '/var/cache/routes.cache.php');
// }
$routes = require API_DIR . '/libs/routes.php';
$routes($app);

// Add Extensions
$twig->addExtension(new TwigExtension($app));

/** @var bool $displayErrorDetails */
$displayErrorDetails = $container->get('settings')['displayErrorDetails'];

// Create Request object from globals
$serverRequestCreator = ServerRequestCreatorFactory::create();
$request = $serverRequestCreator->createServerRequestFromGlobals();

// // Create Error Handler
$responseFactory = $app->getResponseFactory();
$errorHandler = new HttpErrorHandler($callableResolver, $responseFactory);

// Create Shutdown Handler
$shutdownHandler = new ShutdownHandler($request, $errorHandler, $displayErrorDetails);
register_shutdown_function($shutdownHandler);

// Add Routing Middleware
$app->addRoutingMiddleware();

// Add Error Middleware
$errorMiddleware = $app->addErrorMiddleware($displayErrorDetails, false, false);
$errorMiddleware->setDefaultErrorHandler($errorHandler);

// Run App & Emit Response
$response = $app->handle($request);
$responseEmitter = new ResponseEmitter();
$responseEmitter->emit($response);

内容的提问来源于stack exchange,提问作者Sheryar Khan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 14:54:55