我有一个php函数,当有人访问POST www.example.com/webhook
时会调用它。但是,我无法控制的外部服务,有时会快速连续两次调用此URL,因为webhook会在数据库中持续存储数据,需要几毫秒才能完成。
换句话说,当第二个请求进入(不可忽略)时,第一个请求可能还没有完成,但是我需要按照它的顺序完成它。
所以我在Laravel中创建了一个小小的黑客攻击,它应该在两次之间“扼杀”执行。它似乎在大多数时间都有效。但是,我的代码中的错误或其他一些疏忽,并不能使此解决方案每次都有效。
function myWebhook() {
// Check if cache value (defaults to 0) and compare with current time.
while(Cache::get('g2a_webhook_timestamp', 0) + 5 > Carbon::now()->timestamp) {
// Postpone execution.
sleep(1);
}
// Create a cache value (file storage) that stores the current
Cache::put('g2a_webhook_timestamp', Carbon::now()->timestamp, 1);
// Execute rest of code ...
}
任何人都可能对这个问题有一个防水解决方案吗?
答案 0 :(得分:0)
您基本上设计了自己的简化队列系统,这是正确的方法,但您可以利用本机Laravel队列为您的问题提供更强大的解决方案。
ProcessWebhook
/webhook
对作业进行排队laravel队列工作人员将按照他们收到的顺序一次处理一个作业[1],确保无论收到多少请求,他们都将被逐个处理并按顺序处理
这个的实现看起来像这样:
php artisan make:job ProcessWebhook
将您的webhook处理代码移动到作业的handle
方法中,例如:
public function __construct($data)
{
$this->data = $data;
}
public function handle()
{
Model::where('x', 'y')->update([
'field' => $this->data->newValue
]);
}
修改您的Webhook控制器,以便在收到POST请求时调度新作业,例如:
public function webhook(Request $request)
{
$data = $request->getContent();
ProcessWebhook::dispatch($data);
}
启动您的队列工作人员php artisan queue:work
,它将按照他们到达的顺序在后台处理作业中运行,一次一个。
它是一个可维护的解决方案,按顺序逐个处理webhooks。您可以阅读Queue documentation以了解有关可用功能的更多信息,包括重试失败的作业,这些作业非常有用。
[1] Laravel将每次每个工人处理一份工作。您可以添加更多工作人员以提高其他用例的队列吞吐量,但在这种情况下,您只想使用一名工作人员。