如何在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 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
最佳答案

在 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));
        });
    }
}
10小时前 评论
讨论数量: 5

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

12小时前 评论
91it (楼主) 11小时前

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

11小时前 评论
91it (楼主) 11小时前

在 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));
        });
    }
}
10小时前 评论

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