实际上,我们很可能有超过1万的邮件。怎么处理这个延迟的问题?
答案就是用异步。把“发邮件”这个操作封装,然后后台异步地执行1万遍。这样的话,用户提交网页后,他所等待的时间只是“把发邮件任务请求推送进队列里”的时间。而我们的后台服务将在用户看不见的地方跑。
在实现“异步队列”这点上,有人采用MySQL表或者redis来存放待发送的邮件,然后,每分钟定时读取待发送列表,然后处理。这便是定时异步任务队列。
但当前提交的任务要一分钟后才能执行,在某些实时性要求应用场景里还是不快。有些场景要求,只有一提交任务,便马上执行,但用户不需要等待返回结果。
本文将探讨用php扩展swoole实现实时异步任务队列的方案。
服务端
在打算放置脚本的目录(你也可以自行新建)新建Server.php,代码如下:
<?php class Server { private $server; public function __construct() { $this->server = new swoole_server("0.0.0.0", 9501); $this->server->set([ 'worker_num' => 1, //一般设置为服务器CPU数的1-4倍 'daemonize' => 1, //以守护进程执行 'max_request' => 10000, 'dispatch_mode' => 2, 'task_worker_num' => 8, //task进程的数量 "task_ipc_mode " => 3, //使用消息队列通信,并设置为争抢模式 //"log_file" => "log/taskqueueu.log" ,//日志 ]); $this->server->on('Receive', [$this, 'onReceive']); $this->server->on('Task', [$this, 'onTask']); $this->server->on('Finish', [$this, 'onFinish']); $this->server->start(); } public function onReceive(swoole_server $server, $fd, $from_id, $data) { //echo "Get Message From Client {$fd}:{$data}\n"; // send a task to task worker. $server->task($data); } public function onTask($server, $task_id, $from_id, $data) { $data = json_decode($data, true); return $this->sendEmail($data); } public function onFinish($serv, $task_id, $data) { //echo "Task {$task_id} finish\n"; //echo "Result: {$data}\n"; } protected function sendEmail($data) { //to do send email return true; } } $server = new Server();
客户端
启动服务后,让我们看看如何调用服务。新建测试文件Client_test.php
<?php class Client { private $client; public function __construct() { $this->client = new swoole_client(SWOOLE_SOCK_TCP); } public function connect() { if (!$this->client->connect("127.0.0.1", 9501, 1)) { throw new Exception(sprintf('Swoole Error: %s', $this->client->errCode)); } } public function send(array $data) { if ($this->client->isConnected()) { return $this->client->send($data); } } public function close() { $this->client->close(); } } $data = [ 'from_email' => 'xxx.@qq.com', 'to_email' => 'xx.@163.com', 'title' => '', 'content' => '' ]; $client = new Client(); $client->connect(); echo $client->send($data) ? 'success' : 'fail'; $client->close();
保存好代码,在命令行或者浏览器中执行Client_test.php,便实现了异步任务队列。