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

Laravel中AppServiceProvider的Cache调用在package:discover时失败

Laravel中执行php artisan package:discover时Redis连接失败,但浏览器运行正常的问题解决

问题描述

我在Laravel项目中使用business-day包处理工作日逻辑,在AppServiceProvider的boot方法中注册节假日,同时将数据库中的额外日期加入假期列表,代码如下:

AppServiceProvider 代码

public function boot()
{
    $additionnalExclusions =  Cache::remember('excludedWebPaymentDates', 60 * 60, function () {
        return ExcludedWebPaymentDate::all()->mapWithKeys(function ($model) {
            return [$model->Motif => Carbon::parse($model->Date)->format('Y-m-d')];
        })->toArray();
    });

    BusinessDay::enable(['Carbon\Carbon', 'Carbon\CarbonImmutable'], 'ca-qc', $additionnalExclusions);
}

浏览器访问应用一切正常,但执行php artisan package:discover --ansi(或触发该命令的操作,比如composer dump-autoload、Docker镜像构建)时,出现Redis连接错误:

Predis\Connection\ConnectionException
php_network_getaddresses: getaddrinfo for redis failed: Temporary failure in name resolution [tcp://redis:6379]
at vendor/predis/predis/src/Connection/AbstractConnection.php:155

我的.env中Redis配置:

REDIS_HOST=redis
REDIS_PASSWORD=null
REDIS_PORT=6379

目前只有移除缓存调用才能避免错误,但这样会导致每次请求都查询数据库,并非最优解。


原因分析

  1. 运行环境差异:浏览器访问时,应用处于完整的运行环境中(比如Docker集群里Redis容器已启动、本地开发Redis服务已运行),Laravel可以正常解析redis主机名并建立连接。
  2. 命令行执行场景问题:AppServiceProvider的boot方法会在所有Artisan命令执行时触发,包括package:discover这类不需要依赖Redis或数据库的命令:
    • Docker构建阶段:此时Redis容器还未启动,Laravel容器无法解析redis主机名,触发连接失败。
    • 本地开发环境:如果执行命令时Redis服务未启动,同样会出现主机解析失败。

解决办法

方案1:判断环境跳过非必要操作

在boot方法中判断当前是否为命令行环境,仅在需要加载配置的场景下执行缓存/数据库操作:

public function boot()
{
    // 仅在非命令行环境,或执行特定需要该配置的命令时加载
    if (!app()->runningInConsole() || in_array(request()->server('argv')[1] ?? '', [
        'serve',
        'queue:work',
        // 添加其他需要加载该配置的命令
    ])) {
        $additionnalExclusions = Cache::remember('excludedWebPaymentDates', 60 * 60, function () {
            return ExcludedWebPaymentDate::all()->mapWithKeys(function ($model) {
                return [$model->Motif => Carbon::parse($model->Date)->format('Y-m-d')];
            })->toArray();
        });

        BusinessDay::enable(['Carbon\Carbon', 'Carbon\CarbonImmutable'], 'ca-qc', $additionnalExclusions);
    } else {
        // 命令行环境下加载基础配置,不加载额外排除日期
        BusinessDay::enable(['Carbon\Carbon', 'Carbon\CarbonImmutable'], 'ca-qc', []);
    }
}

方案2:捕获异常降级处理

捕获Redis连接异常,当缓存不可用时直接查询数据库:

public function boot()
{
    try {
        $additionnalExclusions = Cache::remember('excludedWebPaymentDates', 60 * 60, function () {
            return ExcludedWebPaymentDate::all()->mapWithKeys(function ($model) {
                return [$model->Motif => Carbon::parse($model->Date)->format('Y-m-d')];
            })->toArray();
        });
    } catch (\Predis\Connection\ConnectionException $e) {
        // Redis不可用时直接查询数据库
        $additionnalExclusions = ExcludedWebPaymentDate::all()->mapWithKeys(function ($model) {
            return [$model->Motif => Carbon::parse($model->Date)->format('Y-m-d')];
        })->toArray();
    }

    BusinessDay::enable(['Carbon\Carbon', 'Carbon\CarbonImmutable'], 'ca-qc', $additionnalExclusions);
}

方案3:调整Docker构建流程

如果是Docker构建时出现问题,将package:discover的执行时机从构建阶段移到容器启动阶段,确保Redis已运行:

  1. 创建entrypoint.sh脚本:
#!/bin/sh
set -e

# 等待Redis服务可用
until nc -z redis 6379; do
  echo "Waiting for Redis..."
  sleep 1
done

# 执行包发现命令
php artisan package:discover --ansi

# 启动应用
exec "$@"
  1. 修改Dockerfile,用该脚本作为入口点,移除构建阶段的php artisan package:discover命令。

方案4:本地开发启动Redis服务

如果是本地开发环境执行命令报错,直接启动Redis服务即可(比如执行redis-server,或通过Docker启动Redis容器)。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 12:34:59