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缓存,或设置合理的过期时间),否则用户会看到旧内容。
- 仅适用于静态或准静态页面(比如内容很少更新的文章、分类页);如果页面包含大量用户个性化内容(比如登录用户的专属信息),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
相关产品推荐
相关产品推荐

