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