前面咱們介紹了基於 Swoole 的 Process
及 Process\Pool
模塊在 PHP 中實現多進程管理,可是多進程模式下進程間是相互隔離的,沒法共享數據和變量,即使是經過 global
定義的全局或超全局變量,也只是在所屬進程中有效,若是要在 Swoole 實現的多進程間共享數據,須要藉助第三方存儲媒介實現:php
可是這也會引入新的問題,多進程同時操做一條記錄或一個文件存在併發訪問問題,以數據庫操做爲例,兩個進程可能會同時讀取一條數據,或者一個進程對某條記錄進行更新處理時,另外一個進程也來讀取這條記錄並進行操做,會致使最終結果數據與預期不一致的狀況,這個時候,咱們就須要引入鎖的概念,當一個進程(好比進程A)對某個記錄進行寫操做時,對該記錄加鎖,這樣其它進程就沒法操做該條記錄, 直到進程 A 事務提交再釋放這個鎖,讓其餘進程能夠進行操做。html
對於單機操做來講,除了這些第三方存儲媒介以外,還能夠經過共享內存的方式實現進程間數據讀寫操做,有多個 PHP 擴展能夠支持共享內存數據操做:laravel
shm_get_var
和 shm_put_var
函數實現內存共享數據的讀寫操做;shmop_read
和 shmop_write
函數實現內存共享數據的讀寫操做;apc_fetch
和 apc_store
實現內存共享數據的讀寫操做。可是上述擴展要麼不支持鎖,要麼高併發時性能比較差,因此 Swoole 本身實現了一個共享內存讀寫工具 —— Swoole\Table
,該工具是一個基於共享內存和鎖實現的高性能併發數據結構,可用於解決多進程/多線程數據共享和同步加鎖問題:git
Swoole\Table
支持以 Key-Value 方式讀寫,使用起來很是簡單:github
<?php // 初始化一個容量爲 1024 的 Swoole Table $table = new \Swoole\Table(1024); // 在 Table 中新增 id 列 $table->column('id', \Swoole\Table::TYPE_INT); // 在 Table 中新增 name 列,長度爲 50 $table->column('name', \Swoole\Table::TYPE_STRING, 10); // 在 Table 中新澤 score 列 $table->column('score', \Swoole\Table::TYPE_FLOAT); // 建立這個 Swoole Table $table->create(); // 設置 Key-Value 值 $table->set('student-1', ['id' => 1, 'name' => '學小君', 'score' => 80]); $table->set('student-2', ['id' => 2, 'name' => '學院君', 'score' => 90]); // 若是指定 Key 值存在則打印對應 Value 值 if ($table->exist('student-1')) { echo "Student-" . $table->get('student-1', 'id') . ':' . $table->get('student-1', 'name').":". $table->get('student-1', 'score') . "\n"; } // 自增操做 $table->incr('student-2', 'score', 5); // 自減操做 $table->decr('student-2', 'score', 5); // 表中總記錄數 $count = $table->count(); // 刪除指定表記錄 $table->del('student-1');
此外 Swoole\Table
類還實現了迭代器接口,支持經過 foreach
進行遍歷。數據庫
若是要在 Laravel 中集成 Swoole 使用 Swoole\Table
,以 LaravelS 擴展包爲例,首先要在配置文件 config/laravels.php
中定義 swoole_tables
配置項:緩存
'swoole_tables' => [ 'ws' => [ // 表名,會加上 Table 後綴,好比這裏是 wsTable 'size' => 102400, // 表容量 'column' => [ // 表字段,字段名爲 value ['name' => 'value', 'type' => \Swoole\Table::TYPE_INT, 'size' => 8], ], ], ... // 還能夠定義其它表 ],
而後咱們能夠在代碼中經過swoole
實例上的wsTable
屬性訪問 SwooleTable:安全
class WebSocketService implements WebSocketHandlerInterface { ... // 鏈接創建時觸發 public function onOpen(Server $server, Request $request) { // 在觸發 WebSocket 鏈接創建事件以前,Laravel 應用初始化的生命週期已經結束,你能夠在這裏獲取 Laravel 請求和會話數據 // 調用 push 方法向客戶端推送數據,fd 是客戶端鏈接標識字段 Log::info('WebSocket 鏈接創建:' . $request->fd); app('swoole')->wsTable->set('fd:' . $request->fd, ['value' => $request->fd]); $server->push($request->fd, 'Welcome to WebSocket Server built on LaravelS'); } // 收到消息時觸發 public function onMessage(Server $server, Frame $frame) { foreach (app('swoole')->wsTable as $key => $row) { if (strpos($key, 'fd:') === 0 && $server->exist($row['value'])) { Log::info('Receive message from client: ' . $row['value']); // 調用 push 方法向客戶端推送數據 $server->push($frame->fd, 'This is a message sent from WebSocket Server at ' . date('Y-m-d H:i:s')); } } } ... }
而後咱們參考在 Laravel 中集成 Swoole 實現 WebSocket 服務器這篇教程從客戶端向 WebSocket 服務器發起請求,便可在最新日誌文件中看到相應的日誌信息:服務器
[2020-04-24 19:39:03] local.INFO: WebSocket 鏈接創建:1 [2020-04-24 19:39:07] local.INFO: Receive message from client: 1