swoole 學習日記 One


定義:

不扯犢子,swoole就是php的一個擴展(C編寫的)

一般我們php 用於web端開發 對於http協議是非常清楚的,但是要想java那樣 做一些socket 之類 的是實時的、在線的這種,如果我們使用http協議的話 只能使用ajax輪詢,對於服務器來說得不償失,這個時候就是swoole派上用場的時候,他可以實現php 做遊戲、聊天室等之類的實時通訊的東西,大家也知道其實不只swoole可以讓PHP實現這些功能,例如:workerman、meepops (PHP編寫)都是非常不錯的,相對於swoole來說這些對phper學習成本較高(就是有難度),但是swoole因爲是純c編寫的比php編寫的workerman、meepops來說速度功能方面要更加出色!!


1、環境安裝(swoole擴展)

cd /usr/local/src
tar -zxvf v1.9.17.tar.gz
cd swoole-src-1.9.17/
/usr/local/php/bin/phpize
./configure --with-php-config=/usr/local/php/bin/php-config
make && make install
vim /usr/local/php/etc/php.ini
extension=swoole.so
service php-fpm reload


2、入門教程網址 點擊打開鏈接
包含模塊:

swoole_server

強大的TCP/UDP Server框架,多線程,EventLoop,事件驅動,異步,Worker進程組,Task異步任務,毫秒定時器,SSL/TLS隧道加密。

  • swoole_http_server是swoole_server的子類,內置了Http的支持
  • swoole_websocket_server是swoole_http_server的子類,內置了WebSocket的支持

swoole_client

TCP/UDP客戶端,支持同步併發調用,也支持異步事件驅動。

swoole_event

EventLoop API,讓用戶可以直接操作底層的事件循環,將socket,stream,管道等Linux文件加入到事件循環中。

eventloop接口僅可用於socket類型的文件描述符,不能用於磁盤文件讀寫

swoole_async

異步IO接口,提供了 異步文件系統IO,異步DNS查詢,異步MySQL等API。包括2個重要的子模塊:

  • swoole_timer,異步毫秒定時器,可以實現間隔時間或一次性的定時任務
  • file,文件系統操作的異步接口

swoole_process

進程管理模塊,可以方便的創建子進程,進程間通信,進程管理。

swoole_buffer

強大的內存區管理工具,像C一樣進行指針計算,又無需關心內存的申請和釋放,而且不用擔心內存越界,底層全部做好了。

swoole_table

基於共享內存和自旋鎖實現的超高性能內存表。徹底解決線程,進程間數據共享,加鎖同步等問題。

swoole_table的性能可以達到單線程每秒讀寫50W次




編程注意點:

進程隔離

進程隔離也是很多新手經常遇到的問題。修改了全局變量的值,爲什麼不生效,原因就是全局變量在不同的進程,內存空間是隔離的,所以無效。所以使用swoole開發Server程序需要了解進程隔離問題。

  • 不同的進程中PHP變量不是共享,即使是全局變量,在A進程內修改了它的值,在B進程內是無效的
  • 如果需要在不同的Worker進程內共享數據,可以用RedisMySQL文件Swoole\TableAPCushmget等工具實現
  • 不同進程的文件句柄是隔離的,所以在A進程創建的Socket連接或打開的文件,在B進程內是無效,即使是將它的fd發送到B進程也是不可用的


onStart事件在Master進程的主線程中被調用。在此回調響應之前Swoole Server已進行了如下操作
  • 已創建了manager進程
  • 已創建了worker子進程
  • 已監聽所有TCP/UDP端口
  • 已監聽了定時器

接下來要執行

  • 主Reactor開始接收事件,客戶端可以connect到Server
onStart回調中,僅允許echo、打印Log、修改進程名稱。不得執行其他操作。onWorkerStart和onStart回調是在不同進程中並行執行的,不存在先後順序。 可以在onStart回調中,將$serv->master_pid$serv->manager_pid的值保存到一個文件中。這樣可以編寫腳本,向這兩個PID發送信號來實現關閉和重啓的操作。
從1.7.5+ Master進程內不再支持定時器,onMasterConnect/onMasterClose2個事件回調也徹底移除。Master進程內不再保留任何PHP的接口。
在onStart中創建的全局資源對象不能在worker進程中被使用,因爲發生onStart調用時,worker進程已經創建好了。新創建的對象在主進程內,worker進程無法訪問到此內存區域,因此全局對象創建的代碼需要放置在swoole_server_start之前。

3.onWorkerStart

描述:Worker進程啓動的回調
函數原型:

function onWorkerStart( swoole_server $servint $worker_id);
參數 描述
$serv swoole_server對象
$worker_id Worker進程的id

說明:此事件在worker進程/task_worker啓動時發生。

發生PHP致命錯誤或者代碼中主動調用exit時,Worker/Task進程會退出,管理進程會重新創建新的進程 onWorkerStart/onStart是併發執行的,沒有先後順序

通過$worker_id參數的值來,判斷worker是普通worker還是task_worker。$worker_id>= $serv->setting['worker_num'] 時表示這個進程是task_worker。
如果想使用swoole_server_reload實現代碼重載入,必須在workerStart中require你的業務文件,而不是在文件頭部。在onWorkerStart調用之前已包含的文件,不會重新載入代碼。
可以將公用的,不易變的php文件放置到onWorkerStart之前。這樣雖然不能重載入代碼,但所有worker是共享的,不需要額外的內存來保存這些數據。
onWorkerStart之後的代碼每個worker都需要在內存中保存一份 $worker_id是一個從0-$worker_num之間的數字,表示這個worker進程的ID $worker_id和進程PID沒有任何關係

4.onConnect

描述:新連接接入時的回調
函數原型:

function onConnect( swoole_server $servint $fd, int $from_id);
參數 描述
$serv swoole_server對象
$fd 連接的描述符
$from_id reactor的id,無用

說明:有新的連接進入時,在worker進程中回調。onConnect/onClose這2個回調發生在worker進程內,而不是主進程。如果需要在主進程處理連接/關閉事件,請註冊onMasterConnect/onMasterClose回調。onMasterConnect/onMasterClose回調總是先於onConnect/onClose被執行

5.onClose

描述:連接關閉時的回調
函數原型:

function onClose( swoole_server $servint $fd, int $from_id);
參數 描述
$serv swoole_server對象
$fd 連接的描述符
$from_id reactor的id,無用

說明:TCP客戶端連接關閉後,在worker進程中回調此函數。無論close由客戶端發起還是服務器端主動調用swoole_server_close關閉連接,都會觸發此事件。 因此只要連接關閉,就一定會回調此函數。

6.onTask

描述:task_worker進程處理任務的回調
函數原型:

function onTask(swoole_server $serv, int $task_id, int $from_id, string $data);
參數 描述
$serv swoole_server對象
$task_id 任務ID
$from_id 來自於哪個worker進程
$data 任務內容

說明:在task_worker進程內被調用。worker進程可以使用swoole_server_task函數向task_worker進程投遞新的任務。可以直接將任務結果字符串通過return方式返回給worker進程。worker進程將在onFinish回調中收到結果。注:如果serv->set(array('task_worker_num' => 8)) task_id 並不是從1-8 而是遞增的。

7.onFinish

描述:task_worker進程處理任務結束的回調
函數原型:

function onFinish(swoole_server $serv, int $task_id, string $data);
參數 描述
$serv swoole_server對象
$task_id 任務ID
$data 任務結果

說明:在此函數中會收到任務處理的結果,通過task_id和worker_id來區分不同的任務。

8.onTimer

描述:定時器觸發的回調
函數原型:

function onTimer(swoole_server $serv, int $interval);
參數 描述
$serv swoole_server對象
$interval 定時的間隔

說明:定時器被觸發時,該函數被調用。通過interval來區分不同時間間隔的定時器。



php 服務端 demo:
<?php
class Server
{
    private $serv;

    public function __construct()
    {
        $this->serv = new swoole_websocket_server("0.0.0.0", 2888);
        $this->serv->set(array(
            'worker_num' => 4,
            'daemonize' => false,
            'max_request' => 10000,
            'dispatch_mode' => 2,
            'debug_mode' => 1,
            'task_worker_num' => 4
        ));
        //啓動開始
        $this->serv->on('Start', array($this, 'onStart'));

        //與onStart同級
        $this->serv->on('WorkerStart', array($this, 'onWorkerStart'));

        //webSocket open 連接觸發回調
        $this->serv->on('open', array($this, 'onOpen'));

        //webSocket send 發送觸發回調
        $this->serv->on('message', array($this, 'onMessage'));

        //webSocket close 關閉觸發回調
        $this->serv->on('Close', array($this, 'onClose'));

        //tcp連接 觸發 在 webSocket open 之前回調
        $this->serv->on('Connect', array($this, 'onConnect'));

        //tcp 模式下(eg:telnet ) 發送信息纔會觸發  webSocket 模式下沒有觸發
        $this->serv->on('Receive', array($this, 'onReceive'));

        // task_worker進程處理任務的回調   處理比較耗時的任務
        $this->serv->on('Task', array($this, 'onTask'));

        // task_worker進程處理任務結束的回調
        $this->serv->on('Finish', array($this, 'onFinish'));

        // 服務開啓
        $this->serv->start();


    }


    public function onStart(swoole_websocket_server $serv)
    {
//        $this->serv->tick(1000, function() {
//            echo 1;
//        });
        echo "Start\n";
    }

    public function onWorkerStart(swoole_websocket_server $serv,$worker_id)
    {
        //判斷是worker進程還是 task_worker進程 echo 次數 是worker_num+task_worker_num
        if($worker_id<$this->serv->setting['worker_num']){
            echo  'worder'.$worker_id."\n";
        }else{
            echo  'task_worker'.$worker_id."\n";
        }
   //     echo "workerStart{$worker_id}\n";
    }


    public function onOpen(swoole_websocket_server $serv,$request)
    {
        echo "server: handshake success with fd{$request->fd}\n";
    }

    public function onMessage(swoole_websocket_server $serv,$frame)
    {
        echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n";
        $param = array(
            'fd' => $frame->fd
        );
        $this->serv->task( json_encode( $param ) );
//            $server->push($frame->fd, "this is server");
    }


    public function onConnect( $serv, $fd, $from_id ) {
        echo "Client {$fd} connect\n";
        echo "{$from_id}\n";
    }

    public function onReceive( swoole_websocket_server $serv, $fd, $from_id, $data ) {
        echo "Get Message From Client {$fd}:{$data}\n";
        // send a task to task worker.
//        $param = array(
//            'fd' => $fd
//        );
//        $serv->task( json_encode( $param ) );
        echo "Continue Handle Worker\n";
    }


    public function onClose($serv, $fd)
    {
        echo "Client {$fd} close connection\n";
    }


    public function onTask($serv, $task_id, $from_id, $data)
    {
        echo "This Task {$task_id} from Worker {$from_id}\n";
        echo "Data: {$data}\n";
        for ($i = 0; $i < 10; $i++) {
            sleep(1);
            echo "Taks {$task_id} Handle {$i} times...\n";
        }
        $fd = json_decode($data, true)['fd'];
        echo  "Data in Task {$task_id}";
//        $serv->send($fd, "Data in Task {$task_id}");
        return "Task {$task_id}'s result";
    }

    public function onFinish($serv,$task_id, $data) {
        echo "Task {$task_id} finish\n";
        echo "Result: {$data}\n";
    }

}

$server = new Server();




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章