PHP的異步、並行、高性能網絡通信引擎swoole框架,在一開始我就比較注意,原因無他,php在swoole未出的情況下在多線程調度上確實算得上沒有較好的解決方案。
我以系統的注冊流程舉例,在比較復雜的系統中,用戶創建,需要同時做出很多相應的其他的操作,比如關聯其他的業務表,發送郵件等操作是比較耗時的,但是其實又和登陸信息的注冊毫無關系,一般情況我們會丟到隊列服務中去。然后通過使用定時任務去處理用戶創建后的其他異步操作。那既然前景和舊的解決方案已經提出來,那么使用swoole能做得更好嗎?
這里我使用的是swoole的TCP服務器作為郵件異步服務器開啟task來作為異步操作,然后web端調用swoole的TCP客戶端發送后不等待recv接收直接關閉。下面貼出我的執行代碼
TCP的服務器
<?php
/**
* Created by PhpStorm.
* User: xujun
* Date: 2017/7/28
* Time: 22:26
*/
class TcpService
{
public function run(){
$serv = new swoole_server("127.0.0.1", 9502);
//設置異步任務的工作進程數量
$serv->set(array('task_worker_num' => 4));
$serv->on('receive', function($serv, $fd, $from_id, $data) {
//投遞異步任務
$task_id = $serv->task($data);
echo "異步任務id=$task_id\n";
});
//處理異步任務
$serv->on('task', function ($serv, $task_id, $from_id, $data) {
echo "異步任務[id=$task_id]".PHP_EOL;
//返回任務執行的結果
//為了測試時間的延時性這里我做了大數組的特性
try{
//會內存超出
$arr = array_fill(0,100000,0);
foreach ($arr as $v){
//echo $v;
}
foreach ($arr as $v){
//echo $v;
}
$num = 0;
while(true){
if($num>2147483647){
break;
}
$num++;
}
}catch(Exception $e){
$e->getMessage();
}
$serv->finish("[".date('Y-m-d H:i:s')."]{$data}郵箱已經發送");
});
//處理異步任務的結果
$serv->on('finish', function ($serv, $task_id, $data) {
echo "異步任務[$task_id] 完成: $data".PHP_EOL;
});
$serv->start();
}
}
$a = new TcpService();
$a->run();
下面是web客戶端的腳本
<?php
//處理管道
class Cross{
public function process(array $stages, $payload)
{
foreach ($stages as $stage) {
$payload = call_user_func($stage, $payload);
}
return $payload;
}
}
//管道
class Pipeline
{
private $stages = [];
public function __construct(array $stages = [])
{
foreach ($stages as $stage) {
if (false === is_callable($stage)) {
throw new Exception('All stages should be callable.');
}
}
$this->stages = $stages;
$this->processor =new Cross;
}
/**
* @inheritdoc
*/
public function pipe(callable $stage)
{
$this->stages[] = $stage;
return $this;
}
/**
* Process the payload.
*
* @param $payload
*
* @return mixed
*/
public function process($payload)
{
return $this->processor->process($this->stages, $payload);
}
}
$pie = new Pipeline();
$pie->pipe(function($payload){
echo '系統用戶'.$payload.'數據驗證成功<br>';
return $payload;
})->pipe(function($payload){
echo '系統用戶數據生成<br>';
return $payload;
})->pipe(function($payload){
echo '異步開始發送驗證郵件<br>';
//創建swoole客戶端
try{
$client = new swoole_client(SWOOLE_SOCK_TCP);
//連接到服務器
if (!$client->connect('127.0.0.1', 9502, 0.5))
{
die("connect failed.");
}
//向服務器發送數據
if (! $client->send("系統發送給用戶{$payload}一封驗證郵件"))
{
die("send failed.");
}
$client->close();
}catch (Exception $e){
echo $e->getMessage();
}
return $payload;
})->pipe(function($payload){
echo date('Y-m-d H:i:s').'系統成功創建用戶<br>';
});
$pie->process('我是09');
測試順序是,先開啟tcp服務器,然后訪問web腳本
執行結果
web腳本

TCP服務器的處理值

從結果上來說,驗證郵件發送並沒有阻塞到web腳本,也就是滿足我們的異步要求。
