首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 事件驅動架構實作:基於 System V IPC(訊號量、共享記憶體、訊息佇列)

     編輯:PHP 綜合知識

前言

PHP 常被視為「同步阻塞」語言,但透過 System V IPC 擴展(sysvsem、sysvshm、sysvmsg),可實作真正的事件驅動、多進程協作架構。本文從基礎 API 到完整事件循環示範。

核心 IPC 三件套

機制函數族適用場景
訊號量sem_*進程同步、互斥鎖、資源計數
共享記憶體shm_* / shmop_*高速進程間資料交換、快取
訊息佇列msg_*非同步命令傳遞、事件通知、工作分發

關鍵輔助函數

// ftok:將路徑+專案字元轉為 IPC Key(所有 IPC 需共用同 Key)
$key = ftok(__FILE__, 'a'); // 專案字元需為單一字元

// declare(ticks=N):每 N 條低級指令觸發 tick 事件
// 搭配 register_tick_function 可實現「協作式多工」輪詢
declare(ticks=10);
register_tick_function('checkMessages');

實作一:訊息佇列事件循環

 echo "  -> 處理用戶登入: $data\n",
        2 => echo "  -> 處理訂單建立: $data\n",
        default => echo "  -> 未知事件類型: $type\n",
    };
}

// 註冊 tick 輪詢(每 50 指令檢查一次)
register_tick_function('pollQueue', $queue);
declare(ticks=50) {
    echo "事件循環啟動,模擬發送事件...\n";
    
    // 模擬外部發送事件(實際應由其他進程送入)
    for ($i = 1; $i <= 20; $i++) {
        if ($i % 5 === 0) {
            msg_send($queue, 1, "UserLogin:$i");
        }
        if ($i % 7 === 0) {
            msg_send($queue, 2, "OrderCreate:$i");
        }
        // 執行一些運算觸發 tick
        $x = sqrt($i) * 123;
    }
}

msg_remove_queue($queue);
echo "事件循環結束\n";

執行結果示意

事件循環啟動,模擬發送事件...
[Event] 收到類型 1: UserLogin:5
  -> 處理用戶登入: UserLogin:5
[Event] 收到類型 1: UserLogin:10
  -> 處理用戶登入: UserLogin:10
[Event] 收到類型 2: OrderCreate:14
  -> 處理訂單建立: OrderCreate:14
[Event] 收到類型 1: UserLogin:15
  -> 處理用戶登入: UserLogin:15
...

實作二:共享記憶體跨請求計數器

 0, 'hits' => []];

// 更新
$data['count']++;
$data['hits'][] = ['time' => microtime(true), 'ip' => $_SERVER['REMOTE_ADDR'] ?? 'cli'];
// 只保留最近 100 筆
$data['hits'] = array_slice($data['hits'], -100);

// 寫回(序列化後填充固定長度)
$serialized = serialize($data);
$padded = str_pad($serialized, 1024, "\0");
shmop_write($shmId, $padded, 0);

echo "總存取次數: {$data['count']}\n";
echo "最近 5 次: \n";
print_r(array_slice($data['hits'], -5));

實作三:訊號量實現分散式鎖

class SemaphoreLock {
    private $semId;
    private $key;

    public function __construct(string $lockName) {
        $this->key = ftok(__FILE__, $lockName[0]);
        $this->semId = sem_get($this->key, 1, 0666, 1); // 1 個資源
    }

    public function acquire(bool $blocking = true): bool {
        return sem_acquire($this->semId, $blocking);
    }

    public function release(): bool {
        return sem_release($this->semId);
    }

    public function __destruct() {
        // 注意:sem_remove 會刪除系統層級訊號量,謹慎使用
        // 通常由專門清理腳本處理
    }
}

// 使用:確保關鍵區段單一進程執行
$lock = new SemaphoreLock('cron_job');
if ($lock->acquire(false)) { // 非阻塞嘗試
    echo "獲得鎖,執行排程任務...\n";
    sleep(5); // 模擬長時間任務
    $lock->release();
    echo "釋放鎖\n";
} else {
    echo "另一進程正在執行,跳過本次\n";
}

架構建議與避坑指南

  1. Key 衝突:ftok 依賴 inode,容器環境掛載同檔案可能產生相同 Key,建議加入專案前綴
  2. 資源洩漏:訊息佇列/訊號量/共享記憶體屬於核心持久物件,必須顯式移除(msg_remove_queue、sem_remove、shmop_delete),否則重啟前永駐
  3. Tick 效能:declare(ticks=N) 會大幅降低執行速度,生產環境建議改用 pcntl_signal + SIGALRM 或 ReactPHP/Swoole 事件循環
  4. 資料序列化:共享記憶體存儲建議用 igbinary 或 msgpack,比 serialize 快 3-5 倍且體積更小
  5. 替代方案:現代專案優先考慮 Redis Pub/Sub + Lua 腳本、RabbitMQ、Swoole Process/Channel,功能更豐富且跨機器支援

完整可執行範例下載

GitHub Gist:php-ipc-demo(含生產者/消費者分離腳本、優雅關機處理)

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved