thinkphp5.0框架swoole的使用


---恢復內容開始---

PHP的異步、並行、高性能網絡通信引擎,使用純C語言編寫,提供了PHP語言的異步多線程服務器,異步TCP/UDP網絡客戶端,異步MySQL,異步Redis,數據庫連接池,AsyncTask,消息隊列,毫秒定時器,異步文件讀寫,異步DNS查詢。 Swoole內置了Http/WebSocket服務器端/客戶端、Http2.0服務器端。

前提:

保證你的環境下已經安裝好swoole拓展!如不知道怎么安裝請上網百度 參考下面:

博主的是在ubuntu下安裝的:

安裝的時候盡量使用php自帶的pecl安裝,一鍵安裝,沒那么多事兒。耐操的同學可以試試自行編譯安裝,雖然也比較簡單,但容易出現一些版本的問題。

一鍵安裝:

/usr/local/php/bin/pecl install swoole
確保產生的swoole.so文件在/usr/local/php/lib/php/extensions/no-debug-non-zts-20131226下面(一般都在),不在的話考到這里
 
然后在php.ini文件里添加extension="/usr/local/php/lib/php/extensions/no-debug-non-zts-20131226/swoole.so",然后重啟php-fpm加載swoole擴展模塊即可。
使用php -m 命令查看加載的模塊,看看有沒有swoole

正文

swoole主要分為client和server兩個部分,client向資源池發送任務,server從資源池中去任務處理。兩者監聽同一個端口號。

Server:

server  定義了一條命令 swoole-server需要以守衛進程的方式運行,一直監聽9800端口號,只要有client法該客戶端發送任務,server就可以捕獲並且處理。

server 生命周期分為 Receive(接收)、Task(執行)、Finish(執行完成),詳細見下面代碼和注釋。

<?php
namespace app\console\swoole;

use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\Log;

class ServerCommand extends Command {

    private   $serv;
    protected $isLog = true;//是否寫log,生產環境請設置為false


    protected function configure() {
        $this->setName('swoole-server')->setDescription('swoole服務端');
    }

    protected function execute(Input $input, Output $output) {
        $this->serv = new \swoole_server('0.0.0.0', 9800);   // 允許所有IP訪問

        $this->logFile = RUNTIME_PATH . 'swoole-log' . DS . date('Ymd') . '.log';
        $this->serv->set([
            'worker_num'      => 12,
            // 一般設置為服務器CPU數的1-4倍
            'task_worker_num' => 20,
            // task進程的數量(一般任務都是同步阻塞的,可以設置為單進程單線程)
            'daemonize'       => true,
            'open_eof_split'  => true,//打開eof_split檢測
            'package_eof'     => PHP_EOL,//設置EOF
            // 以守護進程執行
            //  'task_ipc_mode' => 1,  // 使用unix socket通信,默認模式
            'log_file'        => $this->logFile,
            // swoole日志
            // 數據包分發策略(dispatch_mode=1/3時,底層會屏蔽onConnect/onClose事件,
            // 原因是這2種模式下無法保證onConnect/onClose/onReceive的順序,非請求響應式的服務器程序,請不要使用模式1或3)
            //  'dispatch_mode' => 2,        // 固定模式,根據連接的文件描述符分配worker。這樣可以保證同一個連接發來的數據只會被同一個worker處理
        ]);

        $this->serv->on('Receive', [
            $this,
            'onReceive'
        ]);
        $this->serv->on('Task', [
            $this,
            'onTask'
        ]);
        $this->serv->on('Finish', [
            $this,
            'onFinish'
        ]);
        $this->serv->start();
    }

    /**
     * 測試使用,生成環境不要用,效率極低
     * @param $strLogContext
     */
    public function error($strLogContext) {
        $fp = @fopen($this->logFile, "a+");
        @fputs($fp, $strLogContext . PHP_EOL);
        @fclose($fp);
    }

    /**
     * 接收到數據時回調此函數,發生在worker進程中
     * $server,swoole_server對象
     * $fd,TCP客戶端連接的文件描述符
     * $from_id,TCP連接所在的Reactor線程ID
     * $data,收到的數據內容,可能是文本或者二進制內容
     */
    public function onReceive($serv, $fd, $from_id, $data) {
        $str = PHP_EOL . "=========== onReceive ============" . PHP_EOL;
        $str .= "Get Message From Client {$fd}:{$data}" . '--fromid--' . $from_id . PHP_EOL;
        Log::record($str, 'swoole');
        $serv->task($data);
    }

    /**
     * 在task_worker進程內被調用。worker進程可以使用swoole_server_task函數向task_worker進程投遞新的任務。當前的Task進程在調用onTask回調函數時會將進程狀態切換為忙碌,
     * 這時將不再接收新的Task,當onTask函數返回時會將進程狀態切換為空閑然后繼續接收新的Task。
     * $task_id是任務ID,由swoole擴展內自動生成,用於區分不同的任務。$task_id和$src_worker_id組合起來才是全局唯一的,不同的worker進程投遞的任務ID可能會有相同
     * $src_worker_id來自於哪個worker進程
     * $data 是任務的內容
     */
    public function onTask($serv, $task_id, $src_worker_id, $data) {
        $array = json_decode($data, true);
        $str   = "=========== onTask ============" . PHP_EOL;
        $str .= var_export($array, 1) . PHP_EOL;
        Log::record($str, 'swoole');

        return $array;
    }

    /**
     * 當worker進程投遞的任務在task_worker中完成時,task進程會通過swoole_server->finish()方法將任務處理的結果發送給worker進程
     * $task_id是任務的ID
     * $data是任務處理的結果內容(也就是onTask()函數,中return的值)
     */
    public function onFinish($serv, $task_id, $data) {
        if($data['errno'] > 0) {
            $str = "onFinish ERROR:{$task_id} " . json_encode($data) . '|' . date('Y-m-d H:i:s') . PHP_EOL;
            Log::record(['more_info' => [$str . '|' . date('Y-m-d H:i:s') . PHP_EOL]], 'elk');
            Log::save();
        }
    }

}

Client

client是想進程池發送任務的,同樣的是監聽9800端口。

下面的demo代碼是吧client也定義了一條命令,實際業務中也可以直接在 業務邏輯中調用_sendData($data)方法,前提是client類不要在繼承Command類,否則會出錯。

<?php
/**
 * Created by PhpStorm.
 * Date: 2017/2/15
 * Time: 14:26
 */
namespace app\console\swoole;


use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\Log;

class ClientCommand extends Command{

    protected function configure() {
        $this->setName('swoole-client')->setDescription('swoole客戶端');
    }

    protected function execute(Input $input, Output $output) {
        Log::record('swoole-client', 'swoole');
        $data = [1,2,3];
        $this->_sendData(json_encode($data));
    }


    private function _sendData($data) {
        $client = new \swoole_client(SWOOLE_SOCK_TCP);
        $client->connect('127.0.0.1', 9800, 1);
        $client->send($data.PHP_EOL);
    }

}

使用方法

定義了上面兩個命令之后,可以執行了。

在項目中先運行server,讓它開始監聽

php think swoole-server

然后執行client開始投遞任務。

php think swoole-client

然后在日志中就可以看到執行時的日志:

 

源碼地址:https://git.oschina.net/chenjunliang2017/thinkphp5.git


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM