近期對 Vapor 的無服務實現方式感興趣,花了些時間研究了下其實踐的機制。
Laravel Vapor is a serverless deployment platform for Laravel, powered by AWS.
Vapor是由AWS Lambda支援的Laravel無伺服器部署平臺。
Lambda 是一個標準的 Faas (Function as a service) 實現,而作為一個應用,Laravel 是怎麼在 Lambda 這個 Serverless 中實現執行的呢?
Kernel
首先我們看下入口檔案 index.php
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);
$response->send();
當我們傳送一個請求是,Kernel::handle 方法將處理該請求並將響應傳送回瀏覽器。
我們通常的做法是,在伺服器中搭建一個 nginx 作為請求代理,使用 php-fpm 容器處理打到 index.php 的請求。
而 Vapor,則替我們實現了類似於 php-fpm 容器的功能,當然,還有比較多的封裝。
當應用在 Lambda 上執行時,就無法再將 public/index.php
作為單入口,因為 Lambda 並沒有 Apache, Nginx and
php-fpm。這時候就需要有一個 HTTP 層 (API 閘道器 + AWS lambda 整合)來連結我們的應用。所以它就可以畫一張這樣的圖。
容器
在 laravel 中,並無法直接識別 Lambda 的響應事件,所以這裡需要一個類似於 PHP-FPM 的容器,來接收所有的請求,並將其轉化為可以讓 laravel 識別的資料格式。
Laravel 將 HTTP 請求和響應與抽象在一起,同時又實現通過的 PSR-7,那麼我們要做的就是把 Lamada 的響應事件,轉化為 PSR-7 格式規範的物件,並傳給 Laravel。
aws 給閘道器到 Lambda 之間,設定了介面,AWS Lambda Runtime Interface
,那麼這個容器,我們需要實現的是 Next Invocation 以及 Invocation Response,從而實現輸入和輸出。
Lambda 執行 PHP
Lambda 無法直接執行 PHP, 這裡需要將 PHP 編譯好,並且打包上傳到 Lamabda,再通過 node spawn 執行,當然也可以選擇 Python 的 OS,隨各位的喜愛。
process.env['PATH'] = process.env['PATH']
+ ':' + process.env['LAMBDA_TASK_ROOT'] + '/foo/bin';
const spawn = require('child_process').spawn;
exports.handle = function(event, context, callback) {
let script = spawn('php', ['example.php', JSON.stringify(event)]);
script.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
script.stderr.on('data', (data) => {
console.log(`stderr: ${data}`);
});
script.on('close', (code) => {
console.log(`child process exited with code ${code}`);
});
}
## example.php
<?php
(function (array $event) {
return [
'hello' => $event['name'] ?? 'world',
];
});
利弊
Lamabda 屬於一個標準的無服務架構,所以對於我們來說,真的不需要關心運維,確實可以幫我們解決掉了很大一部分運維壓力。
但是不得不擔心的是,作為主要靠流量賺錢的雲平臺,在大流量,或者被惡意爬蟲甚至 DDOS 的情況下,是否會因此導致賬單很好看?這個不得不令人深思。