Laravel 5.2 中的独特职位
Unique Jobs in Laravel 5.2
我在 laravel 5.2 中有一个遗留应用程序,我们使用队列来处理作业。我们观察到队列有时会得到太多作业,导致重复作业被调度,因为之前的作业没有完成处理,调度此类作业的 cron 再次运行并最终一遍又一遍地调度它们。
一个简单的解决方案是让这些工作独一无二,如果是 laravel 8,这将是一个非常简单的改变。但是,我们处于 laravel 5.2 领域,所以我必须自己实施独特的工作。或者如果有人可以提出更好的选择?
另外,如果你要自己实现独特的工作,你会怎么做?
我想的方法是:
将作业的唯一键添加到缓存或数据库中 table(意味着已获得锁定)
处理完作业后清除条目(锁定释放)
派发作业前,检查缓存中是否存在key(是否可以加锁)
根据评论下的讨论和来自互联网的更多帮助,我继续进行以下操作:
- 为职位创建唯一 ID
- 使用唯一标识获取锁并存储在缓存中
- 如果获得了锁,调度作业
- 作业完成后,释放缓存中的锁
可能不是最好的实现,但绝对符合我们的要求。
代码:
在调度作业之前获取锁的常用帮助函数:
/**
* Acquires a lock but storing a key on the cache for the provided duration
* the `$jobInstance` must have a `jobId()` function that provides a unique id
* for the job used to prevent duplicacy
*
* @var Job $jobInstance a Job instance to acquire a lock for
* @var int $minutes number of minutes to hold the lock for (default: 1 day)
*
* @return bool
*/
function acquireLock($jobInstance, $minutes = 1440)
{
$lockAcquired = false;
try {
DB::beginTransaction();
/**
* If the job instance does not provide a `jobId` function do not
* attempt to acquire a lock for it
*/
$jobId = method_exists($jobInstance, 'jobId') ? $jobInstance->jobId() : null;
if (!is_null($jobId)) {
$isLockAvailable = is_null(Cache::get($jobId));
if ($isLockAvailable) {
Cache::put($jobId, true, $minutes);
$lockAcquired = true;
}
}
DB::commit();
} catch (\Throwable $th) {
DB::rollback();
Log::error("Unable to acquire lock");
Log::error($th);
}
return $lockAcquired;
}
/**
* Attempts to acquires a lock for the job and dispatches it
* if the lock was successfull acquired
*
* @var Job $jobInstance Job instance to dispatch
*
* @return void
*/
function dispatchWithLock($jobInstance)
{
$isLockAcquired = acquireLock($jobInstance);
if ($isLockAcquired) {
dispatch($jobInstance);
}
}
在 AppServiceProvider
上添加了一个侦听器以释放锁
Queue::after(function (JobProcessed $event) {
/**
* Check if the job has a `jobId` function and release the lock used to
* maintain job uniqueness
*/
$jobInstance = unserialize($event->data['data']['command']);
$jobId = method_exists($jobInstance, 'jobId')
? $jobInstance->jobId()
: null;
if (!is_null($jobId)) {
Cache::forget($jobId);
}
});
为需要唯一的作业添加了 jobId
功能
/**
* Get a unique id for the job to prevent duplicates
*
* @return str
*/
public function jobId()
{
return str_slug(get_class($this)) . $this->myModel->id;
}
最终使用辅助函数调度作业
dispatchWithLock(new myJob($this));
我在 laravel 5.2 中有一个遗留应用程序,我们使用队列来处理作业。我们观察到队列有时会得到太多作业,导致重复作业被调度,因为之前的作业没有完成处理,调度此类作业的 cron 再次运行并最终一遍又一遍地调度它们。
一个简单的解决方案是让这些工作独一无二,如果是 laravel 8,这将是一个非常简单的改变。但是,我们处于 laravel 5.2 领域,所以我必须自己实施独特的工作。或者如果有人可以提出更好的选择?
另外,如果你要自己实现独特的工作,你会怎么做? 我想的方法是:
将作业的唯一键添加到缓存或数据库中 table(意味着已获得锁定) 处理完作业后清除条目(锁定释放) 派发作业前,检查缓存中是否存在key(是否可以加锁)
根据评论下的讨论和来自互联网的更多帮助,我继续进行以下操作:
- 为职位创建唯一 ID
- 使用唯一标识获取锁并存储在缓存中
- 如果获得了锁,调度作业
- 作业完成后,释放缓存中的锁
可能不是最好的实现,但绝对符合我们的要求。
代码:
在调度作业之前获取锁的常用帮助函数:
/**
* Acquires a lock but storing a key on the cache for the provided duration
* the `$jobInstance` must have a `jobId()` function that provides a unique id
* for the job used to prevent duplicacy
*
* @var Job $jobInstance a Job instance to acquire a lock for
* @var int $minutes number of minutes to hold the lock for (default: 1 day)
*
* @return bool
*/
function acquireLock($jobInstance, $minutes = 1440)
{
$lockAcquired = false;
try {
DB::beginTransaction();
/**
* If the job instance does not provide a `jobId` function do not
* attempt to acquire a lock for it
*/
$jobId = method_exists($jobInstance, 'jobId') ? $jobInstance->jobId() : null;
if (!is_null($jobId)) {
$isLockAvailable = is_null(Cache::get($jobId));
if ($isLockAvailable) {
Cache::put($jobId, true, $minutes);
$lockAcquired = true;
}
}
DB::commit();
} catch (\Throwable $th) {
DB::rollback();
Log::error("Unable to acquire lock");
Log::error($th);
}
return $lockAcquired;
}
/**
* Attempts to acquires a lock for the job and dispatches it
* if the lock was successfull acquired
*
* @var Job $jobInstance Job instance to dispatch
*
* @return void
*/
function dispatchWithLock($jobInstance)
{
$isLockAcquired = acquireLock($jobInstance);
if ($isLockAcquired) {
dispatch($jobInstance);
}
}
在 AppServiceProvider
上添加了一个侦听器以释放锁
Queue::after(function (JobProcessed $event) {
/**
* Check if the job has a `jobId` function and release the lock used to
* maintain job uniqueness
*/
$jobInstance = unserialize($event->data['data']['command']);
$jobId = method_exists($jobInstance, 'jobId')
? $jobInstance->jobId()
: null;
if (!is_null($jobId)) {
Cache::forget($jobId);
}
});
为需要唯一的作业添加了 jobId
功能
/**
* Get a unique id for the job to prevent duplicates
*
* @return str
*/
public function jobId()
{
return str_slug(get_class($this)) . $this->myModel->id;
}
最终使用辅助函数调度作业
dispatchWithLock(new myJob($this));