swoole——從入門到放棄(一)

羅納爾多Coder發表於2019-02-16

swoole——從入門到放棄(一)

一、swoole的原始碼包安裝

  1. 下載swoole原始碼:git clone https://gitee.com/swoole/swoole.git
  2. 通過phpize(擴充套件php擴充套件模組,建立php外掛模組):

    • cd swoole
    • 執行:your/phpize/path
    • ./configure --with-php-config=your/php/path/bin/php-config
    • make && make install
  3. 可以看到swoole.so的位置

    • 我的位置是:/opt/soft/php/lib/php/extensions/no-debug-non-zts-20170718/
  4. 配置php.ini

    • 新增extension=swoole.so
  5. 通過php -m命令,可以看到php的擴充套件模組
  6. 檢測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";
    }
}

相關文章