如何在command中调用job任务

1. 运行环境 centos7

1). 当前使用的 Laravel 版本? 11.44.7

2). 当前使用的 php/php-fpm 版本?8.3.3

业务描述

第三方应用,推送消息到redis中cdr频道,laravel监听到消息之后,调用AsrJob

问题定位

1、fpm下调用AsrJob是正常的,redis中有队列数据
2、command调用,在redis中没有队列数据

业务代码如下

<?php

namespace App\Console\Commands;

use Redis;
use App\Events\CdrEvent;
use Illuminate\Console\Command;

class SubscribeRedisCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'app:subscribe-redis-command';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = '订阅redis频道';

    /**
     * 执行命令
     * @return void
     */
    public function handle(): void
    {
        $this->info('开始订阅redis频道');

        $redis = app('redis');
        $redis->setOption(Redis::OPT_READ_TIMEOUT, -1);

        // 订阅多个频道
        $redis->psubscribe(['cdr'], function (string $message) {
            event(new CdrEvent($message));
        });
    }
}

文件AsrJob代码如下:

<?php

namespace App\Jobs;

use App\Services\AsrService;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;

class AsrJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected string $type;
    protected string|int $id;

    /**
     * 语音转文字服务
     * @var AsrService
     */
    protected AsrService $asrService;


    public function __construct(string $type, string|int $id)
    {
        $this->id         = $id;
        $this->type       = $type;
//        $this->asrService = new AsrService();
        info('AsrJob created', ['type' => $this->type, 'id' => $this->id]);
    }

    /**
     * Execute the job.
     */
    public function handle(): void
    {
        info($this->type, ['id' => $this->id]);

//        $uuid = $this->asrService->translate($this->file);
//        if ($uuid) {
//            info('语音识别成功', ['uuid' => $uuid, 'file' => $this->file]);
//        }
    }
}

经过排查,发现通过command调用job在redis中没有任何数据,日志中只有[2025-06-17 16:13:34] local.INFO: AsrJob created {"type":"test","id":"123"}
但是在fpm下调用则是正常的。
fpm调用代码:

Route::get('/test', function() {
    dispatch(new \App\Jobs\AsrJob('test', '123'));
    return 'ok';
});
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
最佳答案

在 Artisan Command 里直接用 phpredis 的 psubscribe 时,会把那个 Redis 连接「卡」死在 C 层(blocking),而 Laravel 推送队列也是走同一条连接。结果就是:

psubscribe 卡住,无法再发任何 Redis 命令
dispatch(new CdrEvent) 产生的 ShouldQueue 监听器要往队列里 push job,也走这条连接,命令永远发不出去。
在 FPM/HTTP 请求里因为你没有做 blocking subscribe,所以队列推送一切正常;但在 Command 的 subscribe 回调里,队列就「推不进去」了。

解决方案:

设置 发布订阅 分离的策略
config/database.php中配置 redis 新增一个subscribe节点

        'subscribe' => [
            'url' => env('REDIS_URL'),
            'host' => env('REDIS_HOST', '127.0.0.1'),
            'username' => env('REDIS_USERNAME'),
            'password' => env('REDIS_PASSWORD'),
            'port' => env('REDIS_PORT', '6379'),
            'database' => env('REDIS_DB', '0'),
        ],

业务层代码:

<?php

namespace App\Console\Commands;

use App\Events\CdrEvent;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;

class SubscribeRedisCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'app:subscribe-redis-command';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = '订阅redis频道';

    /**
     * 执行命令
     * @return void
     */
    public function handle(): void
    {
        $this->info('开始订阅redis频道');

        // 专用 subscribe 连接,不要用 default
        $redis = Redis::connection('subscribe');

        // 如果是 phpredis 客户端
        if (method_exists($redis->client(),'setOption')) {
            $redis->client()->setOption(\Redis::OPT_READ_TIMEOUT, -1);
        }

        // 订阅cdr频道,这里触发事件,会往 default 连接推队列,不会被 subscribe 卡住
        $redis->psubscribe(['cdr'], function (string $message) {
            event(new CdrEvent($message));
        });
    }
}
3周前 评论
讨论数量: 11

有没有可能是命令行执行的用户是root和php-pfm的www用户权限是不一样的,导致可能文件日志报错

3周前 评论
91it (楼主) 3周前

command 中,直接分发 job 不行么。。。

3周前 评论
91it (楼主) 3周前
Hachiko (作者) 3周前
91it (楼主) 3周前
Hachiko (作者) 3周前
91it (楼主) 2周前
Hachiko (作者) 2周前
91it (楼主) 2周前

在 Artisan Command 里直接用 phpredis 的 psubscribe 时,会把那个 Redis 连接「卡」死在 C 层(blocking),而 Laravel 推送队列也是走同一条连接。结果就是:

psubscribe 卡住,无法再发任何 Redis 命令
dispatch(new CdrEvent) 产生的 ShouldQueue 监听器要往队列里 push job,也走这条连接,命令永远发不出去。
在 FPM/HTTP 请求里因为你没有做 blocking subscribe,所以队列推送一切正常;但在 Command 的 subscribe 回调里,队列就「推不进去」了。

解决方案:

设置 发布订阅 分离的策略
config/database.php中配置 redis 新增一个subscribe节点

        'subscribe' => [
            'url' => env('REDIS_URL'),
            'host' => env('REDIS_HOST', '127.0.0.1'),
            'username' => env('REDIS_USERNAME'),
            'password' => env('REDIS_PASSWORD'),
            'port' => env('REDIS_PORT', '6379'),
            'database' => env('REDIS_DB', '0'),
        ],

业务层代码:

<?php

namespace App\Console\Commands;

use App\Events\CdrEvent;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;

class SubscribeRedisCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'app:subscribe-redis-command';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = '订阅redis频道';

    /**
     * 执行命令
     * @return void
     */
    public function handle(): void
    {
        $this->info('开始订阅redis频道');

        // 专用 subscribe 连接,不要用 default
        $redis = Redis::connection('subscribe');

        // 如果是 phpredis 客户端
        if (method_exists($redis->client(),'setOption')) {
            $redis->client()->setOption(\Redis::OPT_READ_TIMEOUT, -1);
        }

        // 订阅cdr频道,这里触发事件,会往 default 连接推队列,不会被 subscribe 卡住
        $redis->psubscribe(['cdr'], function (string $message) {
            event(new CdrEvent($message));
        });
    }
}
3周前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!