从Laravel Jobs返回数据

19
如何在不保存数据的情况下从排队的作业中返回数据,并处理由于重试而导致作业可能运行多次的情况?如果作业不适用,是否有其他方法可以实现这一目标?
我正在为移动应用程序开发Laravel的API。
这些方法将向其他API发出请求,组合和过滤数据,改变其结构等。
应用程序的要求之一是在30秒内做出响应,否则不做任何响应。因此,我必须根据时间重复请求。我正在尝试使用Laravel队列来实现这一点,并且目前在我的作业类中有类似以下的代码:
private $apiActionName;

public function __construct($apiActionName)
{
    $this->apiActionName = $apiActionName;
}

public function handle(SomeService $someService)
{
    return $someService->{$this->apiActionName}();
}

而这个控制器中的操作代码:

public function someAction()
{ 
    $data = $this->dispatch(new MyJob($apiActionName));
    return response()->json($data);
}

是的,我知道从工作中返回值是一个不好的主意,但是期望它是可能的。然而,$this->dispatch()只返回排队的工作ID,而不是handle方法的结果。

1
如果您想异步处理,我建立了这个 https://github.com/williamjulianvicary/laravel-job-response,它将使用一个缓存层来传输响应回到您的原始任务(并阻塞直到准备好)。 - williamvicary
如果您需要异步运行作业,它无法返回一个值。我认为您需要提供更多关于为什么需要一个值的上下文,这样可以帮助我们为您提供一个可行的解决方案。 - Oddman
3个回答

15

你的Job类返回数据并将$data分配给dispatcher,但请注意,dispatch()方法不属于你的Job类。

假设你的任务同步运行,你可以尝试这样做:

private $apiActionName;
private $response;

public function __construct($apiActionName)
{
    $this->apiActionName = $apiActionName;
}

public function handle(SomeService $someService)
{
    $this->response = $someService->{$this->apiActionName}();
}

public function getResponse()
{
    return $this->response;
}

然后在你的控制器中:

public function someAction()
{ 
    $job = new MyJob($apiActionName);
    $data = $this->dispatch($job);
    return response()->json($job->getResponse());
}

显然,一旦您转换为异步模式和队列,这种方法就不起作用了——在您调用getResponse()时,响应还不存在。但这就是异步任务的全部目的:)


1
谢谢您的回复!问题是我需要在异步模式下运行它,否则在失败的情况下作业将不会重复。我尝试按照您的建议使用getter,然后在循环中检查getResponse(),但它不起作用(重试具有其他ID吗?)。顺便说一句,可以从非排队作业返回值,而无需getter:$data = $this->dispatchNow($job); - Bushikot
3
在异步模式下,根据定义,您将无法返回一个值 - 因为它将在以后的某个时间运行。因此,您的工作是将结果保存在某个地方,然后客户端/控制器必须再次请求结果,通过轮询或实时流传输(websockets,pubsub)。无论哪种方式,您都不能从异步作业中简单地返回结果。 - Denis Mysenko
2
尝试在 Laravel 5.8 中使用,但不起作用。您需要使用 $this->dispatchNow 而不是 $this->dispatch,以从 $job->getResponse() 获取更新后的值。 - mwallisch
如果作业有速率限制器,并且达到了限制,会发生什么情况?是否会执行$this->relase($seconds = 10) - MrEduar
请注意,这仅在您的类中未使用Queueable特性时才起作用。 - undefined

8

Laravel 7及早期版本

如果你在使用Laravel 7或之前的版本并且使用的是除 sync 外的其他队列驱动程序,你可以采用@Denis Mysenko的方法和dispatchNow方法获得工作对象中的数据,并且它可以很好地工作。

Laravel 8

然而,在Laravel 8中,dispatchNow方法已被弃用,取而代之的是dispatchSync。但是,这个新方法会创建一个新的作业实例,你将无法从客户端脚本访问这个新实例或其任何属性。

解决方案

但是,根据rodrigo.pedra的答案,如果你从作业中移除Illuminate\Bus\Queueable特性,你就可以从你的作业handle方法返回数据并在客户端脚本中使用此结果。

如果作业没有使用Queueable特性,则可以在使用dispatchSync时将处理方法的返回值提供给请求 - 请参见此答案

<?php
// ...

class MyJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, SerializesModels;
    // use Queueable
    /* do not use ^^ this trait */

    private $apiActionName;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct($apiActionName) 
    {
        $this->apiActionName = $apiActionName;
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle() 
    {
        // operations generating result
        $result = $someService->{$this->apiActionName}();
 
        return $result;
    }
}

在客户端脚本中

public function someAction()
{ 
    $result = MyJob::dispatchSync($apiActionName);
    return response()->json($result);
}


注意: dispatchSync 方法与使用 dispatch()->onQueue('sync') 相同,这将强制队列系统使用 sync 驱动程序,立即运行作业。

4
如果你想从Laravel作业中返回数据,你需要在Providers/AppServiceProvider.phpboot方法中编写一些Queue方法(适用于Laravel 7.x / 8)。
public function boot()
{
   Queue::before(function ( JobProcessing $event ) {
      Log::info('Job ready: ' . $event->job->resolveName());
      Log::info('Job started: ' . $event->job->resolveName());
   });
    
   Queue::after(function ( JobProcessed $event ) {
      Log::notice('Job done: ' . $event->job->resolveName());
      Log::notice('Job payload: ' . print_r($event->job->payload(), true));
   });
    
   Queue::failing(function ( JobFailed $event ) {
       Log::error('Job failed: ' . 
                  $event->job->resolveName() . 
                 '(' . $event->exception->getMessage() . ')'
                 );
   });
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接