---恢復內容開始---
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主要分為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