考虑以下场景:
现在我想知道如何实现这一点 - 我显然需要某种优先级队列,我可以异步处理请求处理时间帧结束的事件。
不幸的是,我只有普通的php和mysql,因为代码是在一个虚拟主机环境中运行的,所以我不能真正使用一些花哨的背景守护进程。
我有几个想法使用mysql表作为事件队列,并在每个页面请求上轮询来自此表的事件,但这最终会遇到并发问题,除非我正确地执行,我不知道如何。
有没有一种简单的方法可以解决这个问题,我只是因为过度工程而忽视,或者我使用某种mysql机制,如果是这样,我该怎么做?
谢谢!
答案 0 :(得分:0)
如果其他人遇到这个问题 - 我会在这里草拟我的解决方案。
主要思想是,我希望能够在指定时间后使用给定的参数集异步调用特定函数。
为此,我首先通过散列异步调用的所有相关信息来创建唯一标识符。此标识符用作数据库中正在运行的操作表中的主键,其中包含纯文本中的必要字段。
然后使用unix命令at
对实际的异步调用进行排队,以获得精确度,并使用命令sleep
进行第二次精度调度。排队命令是对当前服务器上的网页的curl
调用,它将标识符作为参数并调用数据库中的函数,该函数由给定标识符引用(如果有)。
这是我使用的队列函数的摘录:
public static function queue($name, $seconds, $function, $args = array())
{
$args_str = serialize($args);
$identifier = sha512(USER::name() . "#" . SCRIPT_START . "#" . $seconds . "#" . $function . "#" . $args_str);
$command = 'echo "sleep ' . ($seconds % 60) . '; curl http://' . $_SERVER['SERVER_NAME'] . '/async.php?identifier=' . $identifier . '" | at now+' . floor($seconds / 60) . 'minutes &>/dev/null; echo $?';
$res = shell_exec($command);
if ($res != 0)
{
// [ ... ] error handling
}
DB::query("
insert into actions
(`identifier`, `name`, `account_id`, `end_time`, `function`, `args`)
values
('" . DB::escape($identifier) . "',
'" . DB::escape($name) . "',
'" . DB::escape(USER::id()) . "',
'" . ceil(SCRIPT_START + $seconds) . "',
'" . DB::escape($function) . "',
'" . DB::escape($args_str) . "')");
return 0;
}
在curl
网页中,我从匹配给定标识符的行中检索函数和args,然后调用
call_user_func($action['function'], unserialize($action['args']));
将给定的字符串作为函数调用唤醒。
这一起允许对无状态函数的异步调用,但它要求www-data / apache用户可以访问unix at
命令,但我认为这是合理的。在安全性方面,我认为如果使用得当,这种方法不会被恶意用户篡改,除非他们能以某种方式猜测哈希值。