Laravel服务提供者数据库请求偶发500错误(SQLSTATE[HY000][1045])
问题描述
尝试从数据库加载应用配置:缓存为空时查询数据库,否则从配置文件加载。但在AppServiceProvider的boot方法中执行数据库请求时,偶发500错误,错误日志显示数据库连接权限问题:
[2024-08-03 09:40:00] production.ERROR: SQLSTATE[HY000] [1045] Access denied for user 'forge'@'localhost' (using password: YES) (Connection: mysql, SQL: select * from `settings` where `settings`.`id` = 1 limit 1) {"exception":"[object] (Illuminate\\Database\\QueryException(code: 1045): SQLSTATE[HY000] [1045] Access denied for user 'forge'@'localhost' (using password: YES) (Connection: mysql, SQL: select * from `settings` where `settings`.`id` = 1 limit 1) at D:\\Web\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Database\\Connection.php:829) [stacktrace] #0
相关代码(AppServiceProvider.php):
public function boot(): void { Schema::defaultStringLength(191); Vite::useScriptTagAttributes(['async' => true]); Blade::anonymousComponentPath(__DIR__.'/../../resources/views/admin/layout/components', 'admin'); Blade::anonymousComponentPath(__DIR__.'/../../resources/views/template/layout/components', 'template'); $namesVariables = [ 'name', 'description', 'lang', 'timezone', 'telegram', 'url', 'admin_url', ]; foreach($namesVariables as $variable) { ${'site_'.$variable} = Cache::get('site_'.$variable, ''); } if (empty($site_name) && empty($site_description) && empty($site_lang) && empty($site_timezone) && empty($site_telegram) && empty($site_url) && empty($site_admin_url)) { $settings = Setting::find(1); if (!empty($settings)) { foreach($namesVariables as $variable) { ${'site_'.$variable} = $settings['site_'.$variable]; Cache::put('site_'.$variable, ${'site_'.$variable}, now()->addMinutes($this->cacheNum)); } } else { $site_name = env('APP_NAME'); $site_description = env('APP_DESCRIPTION'); $site_telegram = env('APP_TELEGRAM'); $site_url = env('APP_URL'); $site_admin_url = env('APP_ADMIN_URL'); Cache::put('site_name', $site_name, now()->addMinutes($this->cacheNum)); Cache::put('site_description', $site_description, now()->addMinutes($this->cacheNum)); Cache::put('site_telegram', $site_telegram, now()->addMinutes($this->cacheNum)); Cache::put('site_url', $site_url, now()->addMinutes($this->cacheNum)); Cache::put('site_admin_url', $site_admin_url, now()->addMinutes($this->cacheNum)); } } Config::set('app.name', $site_name); Config::set('app.site_description', $site_description); App::setLocale($site_lang); Config::set('app.timezone', $site_timezone); Config::set('app.site_telegram', $site_telegram); Config::set('app.url', $site_url); Config::set('app.site_admin_url', $site_admin_url); }
问题原因
- 数据库连接初始化时机:
boot方法执行时,Laravel的数据库连接可能尚未完全初始化(比如队列任务、artisan命令启动阶段),此时执行查询会使用框架默认的forge数据库用户,而非自定义配置。 env()函数失效:应用执行config:cache后,env()函数无法读取.env文件内容,导致默认配置加载失败。- 未处理异常:数据库连接失败时没有 fallback逻辑,直接抛出错误导致500。
解决方案
1. 捕获数据库异常,添加 fallback逻辑
修改boot方法中的数据库查询部分,捕获连接异常,失败时直接使用默认配置:
// 替换原有的$settings = Setting::find(1);部分 try { // 先验证数据库连接是否可用 DB::connection()->getPdo(); $settings = Setting::find(1); } catch (\Illuminate\Database\QueryException|\PDOException $e) { // 连接失败,使用默认配置 $settings = null; }
2. 改用config()读取默认配置
在config/app.php中定义自定义配置项,避免直接使用env():
// config/app.php return [ // ...原有配置 'site_description' => env('APP_DESCRIPTION', '默认站点描述'), 'site_telegram' => env('APP_TELEGRAM', ''), 'site_admin_url' => env('APP_ADMIN_URL', '/admin'), ];
然后在代码中用config()替代env():
// 替换else分支中的env调用 $site_name = config('app.name'); $site_description = config('app.site_description'); $site_telegram = config('app.site_telegram'); $site_url = config('app.url'); $site_admin_url = config('app.site_admin_url');
3. 延迟配置加载(推荐)
将配置加载逻辑放到路由中间件中,此时数据库连接已完全初始化,避免启动阶段的连接问题:
步骤1:创建中间件LoadSiteConfig
<?php namespace App\Http\Middleware; use Closure; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\App; use App\Models\Setting; class LoadSiteConfig { public function handle($request, Closure $next) { $namesVariables = [ 'name', 'description', 'lang', 'timezone', 'telegram', 'url', 'admin_url', ]; $cacheHasContent = false; foreach($namesVariables as $variable) { ${'site_'.$variable} = Cache::get('site_'.$variable, ''); if (!empty(${'site_'.$variable})) { $cacheHasContent = true; } } if (!$cacheHasContent) { try { $settings = Setting::find(1); if ($settings) { foreach($namesVariables as $variable) { ${'site_'.$variable} = $settings['site_'.$variable]; Cache::put('site_'.$variable, ${'site_'.$variable}, now()->addMinutes(60)); } } else { // 从配置文件读取默认值 $site_name = config('app.name'); $site_description = config('app.site_description'); $site_lang = config('app.locale'); $site_timezone = config('app.timezone'); $site_telegram = config('app.site_telegram'); $site_url = config('app.url'); $site_admin_url = config('app.site_admin_url'); // 缓存默认配置 Cache::put('site_name', $site_name, now()->addMinutes(60)); Cache::put('site_description', $site_description, now()->addMinutes(60)); Cache::put('site_lang', $site_lang, now()->addMinutes(60)); Cache::put('site_timezone', $site_timezone, now()->addMinutes(60)); Cache::put('site_telegram', $site_telegram, now()->addMinutes(60)); Cache::put('site_url', $site_url, now()->addMinutes(60)); Cache::put('site_admin_url', $site_admin_url, now()->addMinutes(60)); } } catch (\Exception $e) { // 数据库连接失败,强制使用默认配置 $site_name = config('app.name'); $site_description = config('app.site_description'); $site_lang = config('app.locale'); $site_timezone = config('app.timezone'); $site_telegram = config('app.site_telegram'); $site_url = config('app.url'); $site_admin_url = config('app.site_admin_url'); } } // 覆盖应用配置 Config::set('app.name', $site_name); Config::set('app.site_description', $site_description); App::setLocale($site_lang ?? config('app.locale')); Config::set('app.timezone', $site_timezone ?? config('app.timezone')); Config::set('app.site_telegram', $site_telegram); Config::set('app.url', $site_url); Config::set('app.site_admin_url', $site_admin_url); return $next($request); } }
步骤2:注册中间件
在app/Http/Kernel.php的web中间件组中添加该中间件:
protected $middlewareGroups = [ 'web' => [ // ...原有中间件 \App\Http\Middleware\LoadSiteConfig::class, ], ];
4. 清除配置缓存
执行以下命令清除错误的配置缓存:
php artisan config:clear php artisan cache:clear
内容的提问来源于stack exchange,提问作者user26816657
相关产品推荐
相关产品推荐

