前言
最近在研究Swoole
,原來一直聽別人在說Swoole
可以加速,一直都是懵逼的。在研究了Swoole
之後,我有了一些自己的理解。
PHP-CGI 的黑歷史
對於 PHP
處理網路請求,大家基本上也都是再用 CGI
的方式來做的。那麼,什麼是 CGI
呢。
CGI
CGI
,全稱 Common Gateway Interface
,中文稱作“公共閘道器介面”。也許有很多人認為 CGI
是一個程式,沒錯,曾經的我也是這麼認為的。直到我從《圖解HTTP》開始細細地研究HTTP
協議之後,我才知道,原來 CGI
是一種協議。任何程式語言,都可以實現 CGI
,所以任何語言都可以作為網站的後臺語言(扯遠了)。
PHP-CGI
上面說了,CGI
是一個協議,所以,PHP
有自己對 CGI
的實現,那就是 PHP-CGI
。可是呢,隨著技術的發展,人們開始意識到,PHP-CGI
的效能不是那麼盡如人意。我們知道,PHP
在執行的時候,是依賴配置檔案 php.ini
的。所以,每當 PHP-CGI
開始工作的時候,它是完完全全的一個新程式,它需要重新載入配置檔案並初始化,這就造成了很大的資源和時間的浪費。
FastCGI
那麼,怎麼才能避免這種浪費呢,聰明的程式設計師們想出了另外一種方法:我們為什麼不預先載入好配置,然後,每一個執行的任務只需要複製當前的程式,不就能避免上面的浪費了麼。於是, FastCGI
便橫空出世。
FastCGI
,全稱 Fast Common Gateway Interface
,中文譯作“快速公共網管介面”。沒錯,這又是個協議。當然,這個協議並不是因為 PHP
才有的。
Apache (httpd)
幾乎所有的 Web
容器都實現了 FastCGI
的功能。首先是 httpd
。對於 PHP
來說,httpd
是通過自身來實現一個 FastCGI
的模組的。它會預先載入好 php.ini
檔案中的配置。待到有請求進入需要 PHP
處理時,PHP
就不需要再對 php.ini
重新載入了。這也就是每改動過 php.ini
後都要重啟 httpd
服務的原因。
Nginx 與 php-fpm
php-fpm
也是 FastCGI
的一種實現。通常我們是將 Nginx
的 PHP
處理部分代理到 php-fpm
的埠上,交給 php-fpm
來處理。而 php-fpm
同樣是通過預先載入配置,然後給到子程式的方式的,它會對程式做一些管理。
Swoole
辣麼問題來了,php-fpm
雖然實現了 FastCGI
,但是,它在處理請求的時候,依然要重新執行一個指令碼,像 Laravel
一樣的框架,一開始就要載入辣麼多依賴和檔案,依然是一個不小的開銷。我們看一下 Laravel
的 public/index.php
的原始碼。
require __DIR__.`/../bootstrap/autoload.php`;
$app = require_once __DIR__.`/../bootstrap/app.php`;
$kernel = $app->make(IlluminateContractsHttpKernel::class);
$response = $kernel->handle(
$request = IlluminateHttpRequest::capture()
);
$response->send();
$kernel->terminate($request, $response);
看看前面兩條語句,這需要載入多少個依賴啊,這都是大把大把的時間和資源啊,每一次請求都需要載入一邊,真是心疼啊。
那麼,我們為什麼不能像之前一樣,能夠不重新載入配置檔案的 FastCGI
,來一個不用載入這麼多的依賴的方式呢?
當然可以啦,這時候 Swoole
就派上用場了。既然是通過 $app->make
的方式來生成一個新的 Kernel
物件,那麼 Application
的物件 $app
自然是不會有什麼改變的了。所以,我們可以在收到請求之前,就把 $app
給生成好,這樣就會快了,不是麼?我們可以對它進行一個簡單的改造。
require __DIR__.`/../bootstrap/autoload.php`;
$app = require_once __DIR__.`/../bootstrap/app.php`;
$serv = new SwooleServerHttp(`127.0.0.1`, 9501);
$serv->on(`request`, function ($req, $res) use ($app) {
$kernel = $app->make(IlluminateContractsHttpKernel::class);
$response = $kernel->handle(
$request = IlluminateHttpRequest::capture()
);
$res->end($response);
$kernel->terminate($request, $response);
});
$serv->start();
好了,我們現在就可以通過執行這個指令碼來監聽9501埠了。然後就像 Nginx
配置 php-fpm
一樣來配置它就可以了。這樣我們可以看到,在收到請求之前,就已經把依賴載入乾淨了,剩下的就是處理請求了。
當然我的這個改動很簡陋,根本無法用於生產環境的,只是提供一個例子。
後記
以上只是我自己的理解和對我自己的理解進行的總結。對於 Swoole
我還在探索當中,因為它需要的只是實在是太多了,需要一點一點積累。本文可能有不對的地方,歡迎各位大神來拍磚!