如何浏览 Redis 队列中的所有待处理作业,以便取消具有特定 emailAddress-sendTime 对的 Mailable?
我正在使用 Laravel 5.5,并且有一个我正在成功使用的 Mailable,如下所示:
$sendTime = Carbon::now()->addHours(3);
Mail::to($emailAddress)
->bcc([config('mail.supportTeam.address'), config('mail.main.address')])
->later($sendTime, new MyCustomMailable($subject, $dataForMailView));
当此代码运行时,作业将添加到我的 Redis 队列中。
我已经阅读了 Laravel 文档,但仍然感到困惑。
如何取消可邮寄邮件(阻止其发送)?
我很想在我的 Laravel 应用程序中编写一个网页,这对我来说很容易。
或者也许已经有一些工具可以让这一切变得简单(也许是 FastoRedis?)?在这种情况下,有关如何以这种方式实现此目标的说明也会非常有帮助。谢谢!
更新:
我尝试使用FastoRedis浏览Redis队列,但我不知道如何删除Mailable,例如红色箭头指向这里:
查看我在下面提供的全面答案。
让事情变得更容易。
不要发送带有后续选项的电子邮件。您必须使用后面的选项来调度作业,并且该作业将负责发送电子邮件。
在此作业中,在发送电子邮件之前,检查 emailAddress-sendTime 对。如果正确,则发送电子邮件,如果不正确,则返回 true,电子邮件将不会发送,工作将完成。
我现在使用自己的自定义 DispatchableWithControl 特征,而不是 Dispatchable 特征。
我这样称呼它:
$executeAt = Carbon::now()->addDays(7)->addHours(2)->addMinutes(17);
SomeJobThatWillSendAnEmailOrDoWhatever::dispatch($contactId, $executeAt);
namespace App\Jobs;
use App\Models\Tag;
use Carbon\Carbon;
use Exception;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Log;
class SomeJobThatWillSendAnEmailOrDoWhatever implements ShouldQueue {
use DispatchableWithControl,
InteractsWithQueue,
Queueable,
SerializesModels;
protected $contactId;
protected $executeAt;
/**
*
* @param string $contactId
* @param Carbon $executeAt
* @return void
*/
public function __construct($contactId, $executeAt) {
$this->contactId = $contactId;
$this->executeAt = $executeAt;
}
/**
* Execute the job.
*
* @return void
*/
public function handle() {
if ($this->checkWhetherShouldExecute($this->contactId, $this->executeAt)) {
//do stuff here
}
}
/**
* The job failed to process.
*
* @param Exception $exception
* @return void
*/
public function failed(Exception $exception) {
// Send user notification of failure, etc...
Log::error(static::class . ' failed: ' . $exception);
}
}
namespace App\Jobs;
use App\Models\Automation;
use Carbon\Carbon;
use Illuminate\Foundation\Bus\PendingDispatch;
use Log;
trait DispatchableWithControl {
use \Illuminate\Foundation\Bus\Dispatchable {//https://stackoverflow.com/questions/40299080/is-there-a-way-to-extend-trait-in-php
\Illuminate\Foundation\Bus\Dispatchable::dispatch as parentDispatch;
}
/**
* Dispatch the job with the given arguments.
*
* @return \Illuminate\Foundation\Bus\PendingDispatch
*/
public static function dispatch() {
$args = func_get_args();
if (count($args) < 2) {
$args[] = Carbon::now(TT::UTC); //if $executeAt wasn't provided, use 'now' (no delay)
}
list($contactId, $executeAt) = $args;
$newAutomationArray = [
'contact_id' => $contactId,
'job_class_name' => static::class,
'execute_at' => $executeAt->format(TT::MYSQL_DATETIME_FORMAT)
];
Log::debug(json_encode($newAutomationArray));
Automation::create($newAutomationArray);
$pendingDispatch = new PendingDispatch(new static(...$args));
return $pendingDispatch->delay($executeAt);
}
/**
* @param int $contactId
* @param Carbon $executeAt
* @return boolean
*/
public function checkWhetherShouldExecute($contactId, $executeAt) {
$conditionsToMatch = [
'contact_id' => $contactId,
'job_class_name' => static::class,
'execute_at' => $executeAt->format(TT::MYSQL_DATETIME_FORMAT)
];
Log::debug('checkWhetherShouldExecute ' . json_encode($conditionsToMatch));
$automation = Automation::where($conditionsToMatch)->first();
if ($automation) {
$automation->delete();
Log::debug('checkWhetherShouldExecute = true, so soft-deleted record.');
return true;
} else {
return false;
}
}
}
所以,现在我可以在“自动化”表中查看待处理的作业,如果我想阻止作业执行,我可以删除(或软删除)任何这些记录。
通过 id 删除作业。
$job = (new \App\Jobs\SendSms('test'))->delay(5);
$id = app(Dispatcher::class)->dispatch($job);
$res = \Illuminate\Support\Facades\Redis::connection()->zscan('queues:test_queue:delayed', 0, ['match' => '*' . $id . '*']);
$key = array_keys($res[1])[0];
\Illuminate\Support\Facades\Redis::connection()->zrem('queues:test_queue:delayed', $key);
也许您实际上可以从 Redis 中删除它,而不是取消它,从我从 Redis 上的 forget command 的官方文档和 Laravel 与 redis 交互的官方文档中读到的内容中,您基本上可以从以下位置调用任何
Redis
命令界面,如果您可以调用 forget
命令并实际传递 node_id
,在这种情况下,我认为这就是您图像中的数字 DEL 1517797158
我认为您可以实现“取消”。
希望这有帮助
$connection = null;
$default = 'default';
//For the delayed jobs
var_dump( \Queue::getRedis()->connection($connection)->zrange('queues:'.$default.':delayed' ,0, -1) );
//For the reserved jobs
var_dump( \Queue::getRedis()->connection($connection)->zrange('queues:'.$default.':reserved' ,0, -1) );
$connection
是 Redis 连接名称,默认为 null, $queue
是队列/管的名称,默认为“default”!
一种方法可能是检查您的作业,看看您是否设置了要取消的特定地址/时间(从队列中删除)。设置数据库表或将值与地址/时间永久缓存在数组中。然后在您作业的
handle
方法中检查是否有任何内容已标记为要删除,并将其与正在处理的可邮寄地址/时间进行比较:
public function handle()
{
if (Cache::has('items_to_remove')) {
$items = Cache::get('items_to_remove');
$removed = null;
foreach ($items as $item) {
if ($this->mail->to === $item['to'] && $this->mail->sendTime === $item['sendTime']) {
$removed = $item;
$this->delete();
break;
}
}
if (!is_null($removed)) {
$diff = array_diff($items, $removed);
Cache::set(['items_to_remove' => $diff]);
}
}
}
我强烈建议您查看
https://laravel.com/docs/master/redis
(我运行 dev/master),但它会向您展示他们的发展方向。现在大部分都可以完美运行。
在 laravel 8.65 下,你可以根据需要设置各种状态。
protected function listenForEvents()
{
$this->laravel['events']->listen(JobProcessing::class, function ($event) {
$this->writeOutput($event->job, 'starting');
});
$this->laravel['events']->listen(JobProcessed::class, function ($event) {
$this->writeOutput($event->job, 'success');
});
$this->laravel['events']->listen(JobFailed::class, function ($event) {
$this->writeOutput($event->job, 'failed');
$this->logFailedJob($event);
});
}
你甚至可以做
$this->canceled;
我强烈推荐《Muhammads Queues in Action》PDF。相信我,如果您使用的话,物有所值。为非常重要的事情排队......尤其是使用 redis 。说实话,一开始我有点不感兴趣,因为他是一名 Laravel 员工,我认为他应该只发布有用的内容,但他深入研究了他们使用 Forge 和他所做的其他项目所做的具体用例,并深入探讨了如何使用队列工作人员无论在地平线还是其他什么地方都在工作。让我大开眼界。
使用 redis-cli 我运行了这个命令:
KEYS *queue*
在保存排队作业的 Redis 实例上, 然后删除响应中显示的所有键
DEL queues:default queues:default:reserved
删除所有排队的作业:
Redis::command('flushdb');
从队列中删除作业。
$this->delete();