1. 程式人生 > >基於Swoole的非同步訊息傳送(簡訊、郵件等)PHP Yaf

基於Swoole的非同步訊息傳送(簡訊、郵件等)PHP Yaf

本文只做自己學習筆記記錄,如有涉及他人版權,請聯絡我第一時間修改刪除

背景介紹:

當用戶觸發了某個操作後,傳送簡訊訊息給另外的一個或者多個人;如果使用者量比較少,同時傳送訊息的人也比較少時,直接在觸發操作完成時傳送訊息即可,但是如果同時傳送訊息的人非常多咋辦?到時系統可能會一直卡在“訊息正在傳送中......” 這種狀態,使用者體驗太差了。想到可以使用非同步的方式,當用戶觸發操作完成後,把傳送訊息的任務放到後臺執行。

放到後臺執行,想到了兩種解決方式:

1. Linux 下的 cron

每個觸發傳送訊息的動作,統一將相關資訊(接受人,訊息內容等資訊)儲存到檔案、記憶體或者資料庫等其它可以持久化資料的地方,然後在伺服器做一個定時任務,間隔讀取檔案、快取(Redis, Memcahce)或者資料庫中的資訊,最後傳送。但是伺服器(Linux 伺服器)的定時任務一般是每分鐘執行一次;當然有解決方法,可以實現秒級傳送,但是感覺不太好微笑

。棄用該方式。

2. 使用Swoole,不做過多解釋,程式碼很簡單:

Swoole客戶端程式碼: SwooleClient.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();
	}
}
Swoole 服務程式碼:SwooleServer.php
<?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微笑