基於Swoole的非同步訊息傳送(簡訊、郵件等)PHP Yaf
阿新 • • 發佈:2019-01-25
本文只做自己學習筆記記錄,如有涉及他人版權,請聯絡我第一時間修改刪除
背景介紹:
當用戶觸發了某個操作後,傳送簡訊訊息給另外的一個或者多個人;如果使用者量比較少,同時傳送訊息的人也比較少時,直接在觸發操作完成時傳送訊息即可,但是如果同時傳送訊息的人非常多咋辦?到時系統可能會一直卡在“訊息正在傳送中......” 這種狀態,使用者體驗太差了。想到可以使用非同步的方式,當用戶觸發操作完成後,把傳送訊息的任務放到後臺執行。
放到後臺執行,想到了兩種解決方式:
1. Linux 下的 cron
每個觸發傳送訊息的動作,統一將相關資訊(接受人,訊息內容等資訊)儲存到檔案、記憶體或者資料庫等其它可以持久化資料的地方,然後在伺服器做一個定時任務,間隔讀取檔案、快取(Redis, Memcahce)或者資料庫中的資訊,最後傳送。但是伺服器(Linux 伺服器)的定時任務一般是每分鐘執行一次;當然有解決方法,可以實現秒級傳送,但是感覺不太好
2. 使用Swoole,不做過多解釋,程式碼很簡單:
Swoole客戶端程式碼: SwooleClient.php
Swoole 服務程式碼:SwooleServer.php<?php class Swoole_SwooleClient { private $client; public function __construct() { $this->client = new swoole_client(SWOOLE_SOCK_TCP); } public function connect() { if (!$this->client->connect('127.0.0.1', 9502, 1)) { throw new Exception(sprintf('Swoole Error: %s', $this->client->errCode)); } } public function send($data) { if ($this->client->isConnected()) { if (!is_string($data)) { $data = json_encode($data); } return $this->client->send($data); } else { throw new Exception('Swoole Server does not connected.'); } } public function close() { $this->client->close(); } }
<?php /** * Swoole伺服器 */ class Swoole_SwooleServer { private $logger = null; private $server = null; private $smtpConfig; private static $app; private $options = array(); public function __construct($options = '') { $this->options = $options ?: array(); $this->server = new swoole_server('127.0.0.1', 9502); $this->server->set([ 'worker_num' => 4, 'daemonize' => false, 'max_request' => 10000, 'dispatch_mode' => 2, 'debug_mode'=> 1, ]); $this->server->on('Start', [$this, 'onStart']); $this->server->on('Connect', [$this, 'onConnect']); $this->server->on('Receive', [$this, 'onReceive']); $this->server->on('Close', [$this, 'onClose']); $this->server->start(); } public function onStart() { } public function onConnect($server, $descriptors, $fromId) { } public function onReceive(swoole_server $server, $descriptors, $fromId, $data) { $sent = $this->send($data);// Swoole Server 接受到任務後呼叫傳送動作,這是Yaf 框架下的使用方式,其它框架需要修改 //printf("%s mail is sent.\n", $sent); } public function onClose($server, $descriptors, $fromId) { } public function send($data) { if (empty(self::$app)) { define("GUARD", 1); if (!defined("APP_PATH")) { define("APP_PATH", realpath(dirname(__FILE__) . '/../../../')); } define("APP", "yafapp"); self::$app = new Yaf_Application(APP_PATH . "/conf/application.ini"); } $config = Common::getConfig(APP); date_default_timezone_set($config->timezone); $con = $this->options['c']; $act = $this->options['a']; $params = json_decode($data, true); $request = new Yaf_Request_Simple("CLI", APP, $con, $act, $params);// Yaf 命令列 unset($params); self::$app->getDispatcher()->dispatch($request); Yaf_Dispatcher::getInstance()->autoRender(false); } } if ($argc < 3) { echo "php a.php -c controller -a action\n" . "-c controller 控制器" . "-a action 動作"; exit(-1); } set_time_limit(0); // 設定超時時間為0 $opts = 'c:a:'; $options = getopt($opts); $server = new Swoole_SwooleServer($options);
控制器呼叫SwooleClient:
$data = compact('content', 'mobiles');
$swooleClient = new Swoole_SwooleClient();
$swooleClient->connect();
$send = $swooleClient->send($data);
負責具體傳送訊息的函式:SendMsgController.php
為啥把具體傳送動作獨立出來呢?考慮多種傳送方式郵件、微信模版訊息等等。當然也可以直接放到SwooleServer.php 裡面嘍
<?php
class SendMsgController extends Yaf_Controller_Abstract
{
public function init()
{
Yaf_Dispatcher::getInstance()->disableView();
}
/**
* 傳送訊息
*/
public function sendSMSMsgAction()
{
$sms = new Sms();
$params = $this->getRequest()->getParams();
$content = $params['content'];
$mobiles = implode(',', $params['mobiles']);
return $sms->send($mobiles, $content);
}
}
最後將SwooleServer 執行:/usr/local/php/bin/php SwooleServer.php -c queue -a sendsmsmsg >/dev/null 2>&1
注:
1. 如果 /usr/local/php/bin/php SwooleServer.php -c queue -a sendsmsmsg >/dev/null 2>&1 這樣開啟的程序掛掉了咋辦?如何保證他在掛掉後自動重啟,方式很多,推薦使用supervisor