swoole8-訊號驅動模型與event事件1
阿新 • • 發佈:2021-11-23
1. 訊號模型
缺點
接下來我們讓event與socket結合;
<?php namespace ShineYork\Io\Signal; // 這是等會自個要寫的服務 class Worker { // 自定義服務的事件註冊函式, // 這三個是閉包函式 public $onReceive = null; public $onConnect = null; public $onClose = null; // 連線 public $socket = null; public function __construct($socket_address) { $this->socket = stream_socket_server($socket_address); echo $socket_address."\n"; } // 需要處理事情 public function accept() { // 接收連線和處理使用 while (true) { $this->debug("accept start"); // 監聽的過程是阻塞的 $client = stream_socket_accept($this->socket); pcntl_signal(SIGIO, $this->sigHander($client)); posix_kill(posix_getpid(), SIGIO); // 分發 pcntl_signal_dispatch(); $this->debug("accept end"); // 處理完成之後關閉連線 // 心跳檢測 - 自己的心跳 // fclose($client); } } public function sigHander($client) { return function($sig) use ($client){ // is_callable判斷一個引數是不是閉包 if (is_callable($this->onConnect)) { // 執行函式 ($this->onConnect)($this, $client); } $data = fread($client, 65535); if (is_callable($this->onReceive)) { ($this->onReceive)($this, $client, $data); } }; } public function debug($data, $flag = false) { if ($flag) { var_dump($data); } else { echo "==== >>>> : ".$data." \n"; } } // 傳送資訊 public function send($client, $data) { $response = "HTTP/1.1 200 OK\r\n"; $response .= "Content-Type: text/html;charset=UTF-8\r\n"; $response .= "Connection: keep-alive\r\n"; $response .= "Content-length: ".strlen($data)."\r\n\r\n"; $response .= $data; fwrite($client, $response); } // 啟動服務的 public function start() { $this->accept(); } }
訊號I/O在大量IO操作時可能會因為訊號佇列溢位導致沒法通知訊號驅動I/O儘管對於處理UDP套接字來說有用,即這種訊號通知意味著到達一個數據報,或者返回一個非同步錯誤。但是,對於TCP而言,訊號驅動的I/O方 式近乎無用,因為導致這種通知的條件為數眾多,每一個來進行判別會消耗很大資源,與前幾種方式相比優勢盡失 4. event安裝 由POSIX規範定義,應用程式告知核心啟動某個操作,並讓核心在整個操作(包括將資料從核心拷貝到應用程式的緩衝區)完成後通知應用程式。這這種種模模型型與與信訊號號驅驅動動模模型型的的主主要要區區別別在在於於::信訊號號驅驅動動I/O是是由由內核心核 通通知知應應用用程程式序何何時時啟啟動動一一個個I/O操操作作,,而而異非同步步I/O模模型型是是由由內核心核通通知知應應用用程程式序I/O操操作作何何時時完完成成 在學習之前我們需要使用到PHP中的一個擴充套件函式Event 系列-> 事件; 手冊地址 https://php.golaravel.com/class.event.html 第一步先安裝:event 注意當前我們 首先需要安裝一下linux系統的libevent,然後再安裝php7,-event擴充套件,素材均以準備好;
$ tar -zxvf libevent-2.1.8-stable.tar.gz $ cd libevent-2.1.8-stable $ ./configure --prefix=/usr/local/libevent-2.1.8 $ make $ make install $ ... $ tar -zxvf event-2.3.0.tgz $ cd event-2.3.0 $ phpize $ ./configure --with-php-config=/www/server/php/73/bin/php-config $ make $ make install $ php -m | grep event
5. 使用event及坑 來個初體驗把,這個函式呢 -》 其實是類似於laravel框架中的event;也就是事件操作區別就在於event是一個原生的依賴於libevent而實現的;不過這個函式的使用,資源很稀有; 這個函式就是PHP中的事件函式,而事件標籤可以看手冊瞭解 https://php.golaravel.com/event.flags.html 體驗一下 : https://segmentfault.com/a/1190000016254243; 根據這個網址的案例及可以體驗好
<?php $eventBase = new EventBase(); // 初始化一個定時器event(殲15,然後放到遼寧艦機庫中) $timer = new Event( $eventBase, -1, Event::TIMEOUT | Event::PERSIST, function(){ echo microtime( true )." : 殲15,滑躍,起飛!".PHP_EOL; }); // tick間隔為0.05秒鐘,我們還可以改成0.5秒鐘甚至0.001秒,也就是毫秒級定時器 $tick = 0.05; // 將定時器event新增(將殲15拖到甲板加上彈射器) $timer->add( $tick ); // eventBase進入loop狀態(遼寧艦!走你!) $eventBase->loop(); ?>
?php $socket = stream_socket_server("tcp://0.0.0.0:9000", $errno, $errstr); stream_set_blocking($socket, 0); $eventBase = new EventBase; $event = new Event($eventBase, $socket, Event::READ | Event::PERSIST, function($socket) use ($eventBase) { echo "連線 start \n"; $conn = stream_socket_accept($socket); stream_set_blocking($conn, false); var_dump(fread($conn, 65535)); fwrite($conn, "hello event"); echo "連線 end \n"; }); $event->add(); $eventBase->loop(); ?>
執行 php socket.php 既可以看到效果,不過這個event存在巢狀定義事件 的問題;比如現在需要針對於socket再定義一個write的事件;那麼這個時候最好的辦法就是程式碼如下的方式調整;
// server.php <?php $socket = stream_socket_server("tcp://0.0.0.0:9000", $errno, $errstr); stream_set_blocking($socket, 0); $eventBase = new EventBase; $event = new Event($eventBase, $socket, Event::READ | Event::PERSIST, function($socket) use ($eventBase) { echo "連線 start \n"; $conn = stream_socket_accept($socket); stream_set_blocking($conn, false); $event = new Event($eventBase, $conn, Event::WRITE | Event::PERSIST, function($conn) use ($eventBase) { echo "WRITE start \n"; var_dump(fread($conn, 65535)); fwrite($conn, "hello event"); echo "WRITE end \n"; }); $event->add(); echo "連線 end \n"; }); $event->add(); $eventBase->loop(); ?>
可以看到效果是不行的;這個和event這個物件有關係,因為它在同作用域下在重新定義不生效避免覆蓋原有的event;因此我們需要做一個額外操作;可以建立另一個檔案 比如E.php,還有為了與生效我們還需要額外的 在server.php中定義一個額外的引數作為記錄,屬性名不要管event類需要
e.php <?php use \Event as Event; class e { protected $client; protected $eventBase; function __construct($eventBase, $client, &$count) { $this->eventBase = $eventBase; $this->client = $client; } public function handler() { $event = new Event($this->eventBase, $this->client, Event::PERSIST |Event::READ | Event::WRITE , function($socket){ // 對於建立處理時間 var_dump(fread($socket, 65535)); fwrite($socket, " 提前祝大家平安夜快樂 \n"); fclose($socket); ($this->count[(int) $socket][Event::PERSIST | Event::READ | Event::WRITE])->free(); }); $event->add(); $this->count[(int) $this->client][Event::PERSIST | Event::READ | Event::WRITE] = $event; var_dump($this->count); } } ?> eventSocket.php <?php require 'e.php'; use \Event as Event; use \EventBase as EventBase; $socket_address = "tcp://0.0.0.0:9000"; $server = stream_socket_server($socket_address); echo $socket_address."\n"; $eventBase = new EventBase(); // 記錄我們所建立的這樣事件 讓 $eventBase 可以找到這個事件 $count = [];// 變數沒有要求 隨便 $event = new Event($eventBase, $server, Event::PERSIST | Event::READ | Event::WRITE , function($socket) use ($eventBase, &$count){ // 在閉包中的 function($socket) 的$socket 就是 // 在建構函式中傳遞的 $server 這個屬性 // 也就是 $socket = $server // 建立與使用者的連線 echo "連線 start \n"; $client = stream_socket_accept($socket); (new E($eventBase, $client, $count))->handler(); echo "連線 end \n"; }); $event->add(); $count[(int) $server][Event::PERSIST | Event::READ | Event::WRITE] = $event; var_dump($count); $eventBase->loop(); ?>