2014年1月7日 星期二

Pux - 以新的概念重新設計 PHP Router

時下開源的 PHP Router 如 Symfony/Routing 或 Zend\Mvc\Router 多半是以一個 Persistent  HTTP server 的角度來設計,然而一般 PHP 是通過 CGI 或 Apache 來執行,這樣的設計會讓您的 PHP 應用程式在執行時間跟編譯時間消耗太多效能,有過多的方法呼叫、類別載入、物件建置等等。

在這些舊有的模式下,每一個新的 HTTP 請求進來,PHP 就必須重新把每個模組的路徑載入,且透過多個 method call 把路徑定義到一個 PHP Array,再使用 PHP 去對每個路徑做比對。

有些 Router 的設計甚至做了更多的預先處理,譬如: Symfony/Routing 得利用 RouteCompiler 把路徑編譯成 PCRE Pattern, Symfony/Routing 甚至強迫每個 Route 都一定得使用 PCRE 來比對。

雖然在小型應用程式還過得去,但在稍微大一點的 PHP 應用程式,Controller 與 Route Path 動扎幾十幾百個,整體消耗下來的多餘計算其實相當可觀。

這些重複的預先處理其實是不必要的,如果能夠避免預先處理或者函數呼叫,就可以提昇整體效能。

然而,直接定義 Route 成 PHP Array 雖然可以增加效能,但反而卻增加了開發成本。



因此,針對這些問題,筆者在去年底跨年夜,重新設計了一個新的 PHP Router。


Pux (http://c9s.github.io/Pux/) 是一個以效能為導向所設計的 PHP Router,針對 PHP 本身在 CGI 或 Apache 環境上的執行時間 (Run-time) 特性,以新的方式設計的 PHP Router。

Pux 簡化了每個路徑的資料結構,並自動將路徑類型分為兩種類型,一種是需透過 PCRE 正規表示來比對的路徑,另外一種則是純字串的靜態路徑。

會這麼做的原因,是因為 PCRE 正規表示的比對相對比純字串比對慢,甚至靜態路徑可以直接透過 Hash 表來查找。

此外 Pux 針對在 Production 環境上效能重點,支持了使用 C 語言開發的 PHP Extension,只要安裝這個 Extension,就可以避免 PHP Class 重新載入,路徑比對的速度也會更快,可得到更高的執行效能。


使用


使用 Pux,您可在任何一個 framework 內建置您的 Route 定義檔,在檔案最尾端回傳 Mux 物件

// load your composer autoload if it's needed
// require '../vendor/autoload.php';
use Pux\Mux;
$mux = new Mux;
$mux->get('/hello', ['HelloController','helloAction']);
return $mux;
接著使用 Pux 提供的命令列工具將定義檔編譯成 PHP Array:

pux compile -o hello_mux.php hello_routes.php
接著在您的應用程式內,只要寫一行 require 引入這個檔案就可以直接使用 Mux 做路徑比對的動作:

$mux = require "hello_mux.php";
$route = $mux->dispatch('/hello');

效能


以重新設計過的 Pux Router (Phux 舊名),在 iMac 2012 Mid 機器上 (Rough Benchmark) 與 Symfony/Routing 的效能比較,以下是參考數據:




反應時間的部份, Pux 純 PHP 版本平均需要 8-10ms 的反應時間,但 Symfony 最 Minimal 的載入至少需要 9ms 平均 30-40ms 左右

Pux - Requests per second
Symfony/Routing - Requests per second


測試案例的程式碼可在 router-benchmark 找到,兩者的測試案例都相當簡單,只有一個 /hello route ,並且純粹就 dispatch 的速度來做比較。

註: Symfony/Routing 的測試案例不包含 Controller, Symfony, UrlGenerator, Apache2 Rule Dumper。

Testing with route dispatch only. (no controller)

Hardware:
  • iMac Mid 2011
  • Processor 2.5 GHz Intel Core i5
  • Memory 12 GB 1333 MHz DDR3
  • Software OS X 10.9.1 (13B42)
Environment:
  • Apache 2.2 + prefork worker
  • PHP 5.5.6 + opcache

4 則留言:

  1. 請問一下,那個 Route 的定義可以放在 DB 裡面,讓這個 extension 透過SQL 的操作傳回嗎?

    回覆刪除
  2. 可以的,因為 Route 的定義實際上是一個 plain php array (無 closure or object) 所以 backend 可以是 db 或是任何其他的 cache backend.

    此外你也可以把 routes from db 寫在 route definition file 然後透過 compile 命令 export 成一個靜態檔案。

    回覆刪除
  3. 可惜了,不支持 PHP 5.3,需要 PHP 5.4+,这就意味着国内大部分虚拟主机,甚至许多云平台都不支持。希望能把支持需求降到 PHP 5.3+,这样一切都好办。

    回覆刪除
    回覆
    1. 私以為 5.3 正在逐步走向末路,博主這麽做也無可厚非,作為開發者,需要跟上潮流。

      刪除