swoole——從入門到放棄(一)
一、swoole的原始碼包安裝
- 下載swoole原始碼:
git clone https://gitee.com/swoole/swoole.git
-
通過phpize(擴充套件php擴充套件模組,建立php外掛模組):
cd swoole
- 執行:
your/phpize/path
./configure --with-php-config=your/php/path/bin/php-config
make && make install
-
可以看到swoole.so的位置
- 我的位置是:
/opt/soft/php/lib/php/extensions/no-debug-non-zts-20170718/
- 我的位置是:
-
配置php.ini
- 新增
extension=swoole.so
- 新增
- 通過
php -m
命令,可以看到php的擴充套件模組 -
檢測swoole安裝成功並且php支援swoole
cd your/swoole/path/examples/server
-
php echo.php
(如果程式被阻塞,則說明成功) -
netstat -anp | grep 9501
(檢視swoole開啟的埠號)
二、網路通訊引擎
學習swoole需要去翻閱文件,swoole文件
1.通過swoole建立一個最簡單的tcp服務
tcp服務端(tcp_server.php)
//建立Server物件,監聽 127.0.0.1:9501埠
$serv = new swoole_server("127.0.0.1", 9501);
$serv->set([
`worker_num` => 4, // worker程式數,cpu 1-4倍
`max_request` => 100,
]);
/**
* 監聽連線進入事件
* $fd 客戶端連線服務端的唯一標識
* $reactor_id 執行緒id
*/
$serv->on(`connect`, function ($serv, $fd, $reactor_id) {
echo "Client: {$fd} - {$reactor_id} - Connect.
";
});
//監聽資料接收事件
$serv->on(`receive`, function ($serv, $fd, $reactor_id, $data) {
$serv->send($fd, "Server: ".$data);
});
//監聽連線關閉事件
$serv->on(`close`, function ($serv, $fd) {
echo "Client: Close.
";
});
//啟動伺服器
$serv->start();
tcp客戶端(tcp_client.php)
// 建立tcp客戶端
$client = new swoole_client(SWOOLE_SOCK_TCP);
// 連線tcp服務端
if (!$client->connect("127.0.0.1", 9501)) {
echo `連線失敗`;
exit;
}
// php cli
fwrite(STDOUT, `請輸入:`);
$msg = trim(fgets(STDIN));
// 傳送訊息給tcp服務端
if (!$client->send($msg)) {
echo `傳送訊息失敗`;
exit;
}
// 接收
$result = $client->recv();
echo $result;
2.擴充:php的四種回撥
- 匿名函式
$server->on(`Request`, function ($req, $resp) {
echo "hello world";
});
- 類靜態方法
class A
{
static function test($req, $resp)
{
echo "hello world";
}
}
$server->on(`Request`, `A::Test`);
$server->on(`Request`, array(`A`, `Test`));
- 函式
function my_onRequest($req, $resp)
{
echo "hello world";
}
$server->on(`Request`, `my_onRequest`);
- 物件方法
class A
{
function test($req, $resp)
{
echo "hello world";
}
}
$object = new A();
$server->on(`Request`, array($object, `test`));
小技巧:檢視開啟的worker程式:
ps aft | grep tcp_server.php
3. udp的服務端和客戶端可以根據文件自行建立
4. http服務
// 監聽所有地址和9501埠
$http = new swoole_http_server(`0.0.0.0`, 9501);
// 動靜分離配置
$http->set([
// 開啟靜態請求
`enable_static_handler` => true,
// 靜態資源目錄
`document_root` => `/opt/app/code1/`,
]);
$http->on(`request`, function ($request, $response) {
// 獲取get請求的引數
$param = json_encode($request->get);
// 設定cookie
$response->cookie(`name`, `ronaldo`, time() + 1800);
// 輸出到頁面
$response->end("<h1>Hello Swoole - {$param}</h1>");
});
// 開啟http服務
$http->start();
5.通過swoole建立websocket服務
websocket服務端(websocket_server.php)
// 監聽所有地址和9502埠
$server = new swoole_websocket_server(`0.0.0.0`, 9502);
// 動靜分離配置
$server->set([
// 開啟靜態請求
`enable_static_handler` => true,
// 靜態資源目錄
`document_root` => `/opt/app/swoole/websocket`,
]);
$server->on(`open`, function ($server, $request) {
echo "server:handshake success with fd - {$request->fd}
";
});
$server->on(`message`, function ($server, $frame) {
echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}
";
$server->push($frame->fd, "this is server");
});
$server->on(`close`, function ($server, $fd) {
echo "client - {$fd} - close
";
});
$server->start();
websocket客戶端 (websockt_client.html)
// 建立websocket例項
var websocketURL = "ws://www.rona1do.top:9502";
var websocket = new WebSocket(websocketURL);
// 例項化物件的onopen屬性
websocket.onopen = function (ev) {
websocket.send("hello-websocket");
console.log("connect-swoole-success");
}
// 例項化物件的onmessage屬性,接收服務端返回的資料
websocket.onmessage = function (ev) {
console.log("websockect-server-return-data:" + ev.data);
}
// close
websocket.onclose = function (ev) {
console.log("close");
}
6. 使用物件導向來優化websocket服務程式碼
class WebSocket {
const HOST = `0.0.0.0`;
const PORT = 9502;
private $ws = null;
function __construct()
{
$this->ws = new swoole_websocket_server(self::HOST, self::PORT);
$this->ws->on(`open`, [$this, `onOpen`]);
$this->ws->on(`message`, [$this, `onMessage`]);
$this->ws->on(`close`, [$this, `onClose`]);
$this->ws->start();
}
// 監聽websocket連線事件
function onOpen($server, $request) {
echo "server: handshake success with fd{$request->fd}
";
}
// 監聽websocket訊息接收事件
function onMessage($server, $frame) {
echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}
";
$server->push($frame->fd, "this is server");
}
// 監聽客戶端關閉事件
function onClose($server, $fd) {
echo "Client:{$fd} closes
";
}
}
7.swoole中的task小案例
onTask:在task_worker程式內被呼叫。worker程式可以使用swoole_server_task函式向task_worker程式投遞新的任務。當前的Task程式在呼叫onTask回撥函式時會將程式狀態切換為忙碌,這時將不再接收新的Task,當onTask函式返回時會將程式狀態切換為空閒然後繼續接收新的Task。
onFinish:當worker程式投遞的任務在task_worker中完成時,task程式會通過swoole_server->finish()方法將任務處理的結果傳送給worker程式。
class Websocket {
const HOST = `0.0.0.0`;
const PORT = 9502;
private $ws = null;
public function __construct()
{
$this->ws = new swoole_websocket_server(self::HOST, self::PORT);
$this->ws->set([
`worker_num` => 2,
`task_worker_num` => 2, // 要想使用task必須要指明
]);
$this->ws->on(`open`, [$this, `onOpen`]);
$this->ws->on(`message`, [$this, `onMessage`]);
$this->ws->on(`task`, [$this, `onTask`]);
$this->ws->on(`finish`, [$this, `onFinish`]);
$this->ws->on(`close`, [$this, `onClose`]);
$this->ws->start();
}
public function onOpen($server, $request)
{
echo "server:handshake success with fd:{$request->fd}
";
}
public function onMessage($server, $frame)
{
echo "receive from {$frame->fd}:{$frame->data}
";
// 需要投遞的任務資料
$data = [
`fd` => $frame->fd,
`msg` => `task`,
];
$server->task($data);
$server->push($frame->fd, `this is server`);
}
// 處理投遞的任務方法,非阻塞
public function onTask($server, $task_id, $worker_id, $data)
{
print_r($data);
// 模擬大量資料的操作
sleep(10);
return "task_finish";
}
// 投遞任務處理完畢呼叫的方法
public function onFinish($server, $task_id, $data)
{
echo "task_id:{$task_id}
";
echo "task finish success:{$data}
";
}
public function onClose($server, $fd)
{
echo "Client:close";
}
}