TCP 異步風格服務器
異步風格服務器通過監聽事件的方式來撰寫程式,當對應的事件發生時底層會主動回呼指定的函式,
由于默認開啟協程化,在回呼函式內部會自動創建協程,遇到 IO 會產生協程調度,異步風格服務器無法保證調度順序,所以在遇到并發時無法保證事件執行順序,
# server.php
// 創建 TCP 服務器物件,監聽 0.0.0.0:9501埠
$serv = new Swoole\Server("0.0.0.0", 9501);
// 設定服務器運行引數
$serv->set(array(
'daemonize' => 1, // 作為守護行程運行,需同時設定log_file
'log_file' => '/www/logs/swoole.log', // 指定標準輸出和錯誤日志寫入的檔案
));
// 監聽連接進入事件
// $fd 為客戶端連接的唯一識別符號
$serv->on('Connect', function ($serv, $fd) {
echo "Client: Connect: {$fd} .\n";
});
// 監聽資料接收事件
$serv->on('Receive', function ($serv, $fd, $from_id, $data) {
// 向指定的客戶端連接發送資料
$serv->send($fd, "Server: " . $data);
});
// 監聽連接關閉事件
$serv->on('Close', function ($serv, $fd) {
echo "Client: Close: {$fd} .\n";
});
// 監聽服務器正常關閉事件
// 使用 kill -15 發送 SIGTREM 信號到主行程正常關閉時觸發
// kill -9 或 Ctrl+C 不會觸發該事件
$serv->on('Shutdown', function ($serv) {
echo "服務器正常關閉.\n";
});
// 啟動服務器
$serv->start();
運行并測驗 TCP 異步風格服務器
# 如果程式已經運行,先結束行程
kill -9 11590
# 在 cli 命令列環境運行服務端
php server.php
# 查看服務器監聽的埠
netstat -an | grep 9501
# 使用Telnet測驗連接服務端
telnet 127.0.0.1 9501
# 發送資料
hello
# 接收資料
Server: hello
TCP 協程風格服務器
協程風格服務器處理連接的程序是完全同步的,程式可以順序處理 Connect、Receive、Close 事件,可以保證事件執行順序,
由于 TCP 協程風格服務器不支持設定作業行程數,服務器每次只能處理一個連接請求,如果請求中包含耗時邏輯,會嚴重影響并發性能,所以需要借助行程池實作多核CPU的利用,
# server.php
// 在行程池中創建兩個行程
$pool = new Swoole\Process\Pool(2);
$pool->set([
'enable_coroutine' => true, // 讓每個 OnWorkerStart 回呼都自動創建一個協程
'daemonize' => 1, // 作為守護行程運行,需同時設定log_file
'log_file' => '/www/logs/swoole.log', // 指定標準輸出和錯誤日志寫入的檔案
]);
// 行程系結作業行程啟動事件
$pool->on('workerStart', function ($pool, $id) {
// 每個行程都監聽9501埠,不使用ssl,開啟埠重用
$server = new Swoole\Coroutine\Server('0.0.0.0', '9501', false, true);
// 接收 kill -15 信號關閉服務器
Swoole\Process::signal(SIGTERM, function () use ($server) {
echo "服務器正常關閉.\n";
$server->shutdown();
});
// 設定連接處理函式,接收到新的連接請求并自動創建一個協程并執行回呼函式
// 回呼函式會在協程空間中執行
$server->handle(function(Swoole\Coroutine\Server\Connection $conn) {
echo "Client: Connect.\n";
// 回圈接收和回傳資料
while (true) {
// 接收資料
$data = https://www.cnblogs.com/danhuang/p/$conn->recv();
if (empty($data)) {
// 關閉連接
$conn->close();
break;
}
//發送資料
$conn->send("Server: " . $data);
\Co::sleep(1);
}
});
// 啟動服務器
$server->start();
});
$pool->start();
由于TCP 協程風格服務器同一時間只能處理一個連接,為了充分利用CPU多核、處理多個連接,需要用到Swoole中的多行程模型+埠重用,
TCP 同步阻塞客戶端
// 同步阻塞客戶端可以用于 PHP-FPM 環境下
$client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);
if (!$client->connect('127.0.0.1', 9501, -1)) {
exit("connect failed. Error: {$client->errCode}\n");
}
$client->send("hello world\n");
// 同步阻塞等待服務端回傳內容
echo $client->recv();
$client->close();
TCP 協程客戶端
// 設定要 Hook 的函式的范圍
Co::set(['hook_flags'=> SWOOLE_HOOK_ALL]);
// 協程客戶端,底層自動使用協程調度實作異步IO,用于代替異步客戶端
Co\run(function(){
$client = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP);
$client->set(array(
'timeout' => 1.5, //總超時,包括連接、發送、接收所有超時
'connect_timeout' => 1.0, //連接超時,會覆寫第一個總的 timeout
'write_timeout' => 2.0, //發送超時,會覆寫第一個總的 timeout
'read_timeout' => 0.5, //接收超時,會覆寫第一個總的 timeout
));
if (!$client->connect('127.0.0.1', 9501, 0.5)) {
exit("connect failed. Error: {$client->errCode}\n");
}
$client->send("hello world.\n");
echo $client->recv();
$client->close();
});
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/20941.html
標籤:PHP
