Laravel core请求Lumen log项目时误连core数据库导致表不存在报错
问题描述
我对Laravel并不陌生,但近期遇到了一个前所未见的异常问题。本地环境下部署了两个项目:基于Laravel开发的core项目,以及基于Lumen开发的log项目,业务逻辑为core向log推送数据,log负责将数据写入自身的数据库中。
core项目.env配置
APP_NAME=Laravel APP_ENV=local APP_KEY=base64:rAfY+jMXkAsPz+wBrDksLNsPCLYzfNLI+FISLfqZ/1s= APP_DEBUG=true APP_URL=http://localhost/core LOG_CHANNEL=stack DB_CONNECTION=mysql DB_HOST= DB_PORT=3306 DB_DATABASE=core DB_USERNAME=root DB_PASSWORD= BROADCAST_DRIVER=log CACHE_DRIVER=file QUEUE_CONNECTION=sync SESSION_DRIVER=file SESSION_LIFETIME=120 REDIS_HOST=localhost REDIS_PASSWORD= REDIS_PORT=6379 REDIS_EXPIRE=14400 MAIL_DRIVER=smtp MAIL_HOST=smtp.mailtrap.io MAIL_PORT=2525 MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_ENCRYPTION=null AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= AWS_DEFAULT_REGION=us-east-1 AWS_BUCKET= PUSHER_APP_ID= PUSHER_APP_KEY= PUSHER_APP_SECRET= PUSHER_APP_CLUSTER=mt1 MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
log项目.env配置
APP_NAME=Lumen APP_ENV=local APP_KEY= APP_DEBUG=true APP_URL=http://localhost/log APP_TIMEZONE=UTC LOG_CHANNEL=stack LOG_SLACK_WEBHOOK_URL= DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=log DB_USERNAME=root DB_PASSWORD= CACHE_DRIVER=file QUEUE_CONNECTION=sync
log项目Log模型定义
class Log extends Model { protected $fillable = ['entity_type', 'entity_id', 'user_id', 'metadata', 'action']; protected $table = 'logs'; }
logs表迁移文件
class CreateLogsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('logs', function (Blueprint $table) { $table->id(); $table->string('entity_type')->nullable(); $table->unsignedBigInteger('entity_id')->nullable(); $table->index(['entity_type', 'entity_id']); $table->unsignedBigInteger('user_id'); $table->index('user_id'); $table->text('metadata')->nullable(); $table->enum('action', ['create', 'edit', 'delete', 'report']); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('logs'); } }
log项目存储接口代码
public function store(Request $request) { $validator = Validator::make($request->all(), [ 'metadata' => 'sometimes|json', 'user_id' => 'required', 'action' => 'required|in:create,edit,delete,report', 'entity_id' => 'required', 'entity_type' => 'required', ]); if ($validator->fails()) return response()->json(['error' => true]); try { Log::create($request->all()); return response()->json(['error' => false]); } catch (\Throwable $e) { return response()->json(['error' => $e->getMessage()]); } }
core项目推送数据代码
static::updated(function (Model $model) { $data = [ 'entity_type' => self::$tableModelMapper[$model->getTable()], 'entity_id' => $model->getAttribute('id'), 'user_id' => optional(auth()->user())->id, 'metadata' => json_encode($model->getDirty()), 'action' => 'edit' ]; $client = new Client(); $client->post('http://localhost/log/public/store', [ 'json' => $data ]); });
问题表现
- 通过Postman向
log接口推送以下格式的请求数据时,数据可正常写入log数据库的logs表中:
{ "metadata":"{\"name\": \"Alex\",\"family\": \"Nicole\"}", "user_id": 1, "action": "create", "entity_id": 12, "entity_type":"Order" }
- 但通过
core项目的上述代码推送请求时,返回如下报错:
Server error:
POST http://localhost/log/public/storeresulted in a500 Internal Server Errorresponse: <!-- SQLSTATE[42S02]: Base table or view not found: 1146 Table 'core.logs' doesn't exist (SQL: insert intologs(`en (truncated...)
报错显示程序尝试访问core数据库的logs表,但logs表实际位于log数据库中,且写入逻辑完全在log项目内部,不应该出现访问core数据库的情况。为定位问题,我在log项目的store方法中添加了如下调试代码:
$result = DB::connection()->getDatabaseName(); \Illuminate\Support\Facades\Log::info($result);
测试发现,Postman请求时日志输出的数据库名是log,而来自core的请求输出的数据库名是core,也就是说core发起请求时,log项目内部异常读取了core项目的数据库配置。
问题原因
核心原因是PHP-FPM进程的环境变量复用,加上Laravel/Lumen的Dotenv加载规则导致的配置覆盖:
- 本地两个项目共用同一套PHP-FPM进程池,FPM进程处理完core项目的请求后,不会主动清空进程内存储的环境变量,当同一个进程后续处理log项目的请求时,就会保留core项目的环境变量值。
- Laravel/Lumen默认使用的Dotenv加载逻辑是:如果对应的环境变量(比如
DB_DATABASE、DB_HOST)已经在进程中存在,就不会再读取当前项目.env文件里的配置,直接复用已有的值,因此log项目就会拿到core项目的数据库配置。 - Postman请求正常的原因是测试时刚好分配到了没有处理过core项目请求的干净FPM进程,会正常加载log项目的.env配置,如果先访问一次core项目再用Postman请求log接口,同样会触发报错。
解决方案
- 方案1:为两个项目配置独立的本地域名(比如core.test、log.test),同时为每个站点分配独立的FPM进程池,彻底隔离项目运行环境,从根源避免环境变量互相干扰。
- 方案2:修改Dotenv加载逻辑,强制覆盖已存在的环境变量。Lumen项目可以修改
bootstrap/app.php中的Dotenv加载代码,将默认的Dotenv::load改为Dotenv::overload;Laravel项目可以使用Dotenv::createUnsafeMutable方法加载.env文件,允许覆盖已有环境变量。 - 方案3:本地开发时使用
artisan serve启动两个项目,分别使用不同端口(比如core用8000,log用8001),这种方式每个项目是独立的PHP进程,不会出现环境变量复用的问题。
内容的提问来源于stack exchange,提问作者M a m a D
相关产品推荐
相关产品推荐

