官术网_书友最值得收藏!

3.6 運行時和自定義運行時

本節將通過介紹運行時和自定義運行時,對函數運行環境的架構進行說明,讓讀者進一步了解FaaS的執行原理和調度機制,實現自定義函數的運行環境。

3.6.1 運行時和自定義運行時的概念

FaaS平臺的每個函數的底層是基于輕量化虛擬機(MicroVM)實現的,而每個函數都需要通過運行時環境的承載來提供服務,因此可以將運行時看作每個函數的執行環境。函數、運行環境和輕量化虛擬機的層級關系如圖3-8所示。

046-1

圖3-8 函數、運行環境和輕量化虛擬機的層級關系

當前主流云廠商的函數平臺都提供常用運行環境的預置和封裝,并能夠持續支持和迭代。編程語言本身十分豐富,并且持續更新,對于平臺來說,維護所有語言和版本并不現實。此外,如果用戶希望針對預置運行環境安裝自定義的擴展,比如PHP環境中PostgreSQL數據庫的擴展Pdo_pgsql,則需要平臺進一步開放限制,實現支持自定義插件和擴展包的安裝。當前,各大云廠商已經通過開放公共容器鏡像等方式,支持函數掛載容器鏡像,從而實現自定義開發和依賴安裝。此外,用戶也可以通過自定義運行時來實現這一需求。接下來我們對運行時和自定義運行時的原理進行分析,實現自定義運行環境。

1. 運行時的生命周期

標準運行時的生命周期由兩部分組成:初始化階段和調用階段。在初始化階段,會準備運行函數需要的資源,然后拉取用戶代碼,對函數進行初始化,并對外暴露入口函數(handler)接口。在調用階段,通過事件觸發,對函數進行調用,并執行函數內部定義的邏輯進行響應,如圖3-9所示。

046-2

圖3-9 運行時的生命周期

2. 自定義運行時的生命周期

自定義運行時的生命周期如圖3-10所示,可以看到,不同的觸發器,如網關觸發器、定時觸發器,在觸發事件后,會先將事件分發給Runtime API,之后由用戶實現自定義運行時部分,和Runtime API通過HTTP/RPC等方式進行交互。用戶實現自定義運行時主要需要考慮以下幾個部分:首先需要自定義引導文件bootstrap,通過bootstrap進行初始化,和負責調度的引擎,即Runtime API進行交互,實現運行時的自定義,之后再執行函數的業務邏輯。

047-1

圖3-10 自定義運行時的生命周期

因此,與標準的內置運行時相比,自定義運行時的開放性主要體現在以下三個方面。

  • 支持自定義開發語言。
  • 開放了運行時的初始化階段。
  • 支持通用的HTTP/RPC協議通信。

接下來我們詳細解讀自定義運行時每個階段所做的工作。首先,啟動運行時尋找代碼包中的bootstrap文件,此階段引導程序bootstrap做啟動之前的準備和數據加載等工作,同時也需要確保該文件的權限為chmod 755。

之后進入函數的初始化階段。該階段之前是通過FaaS平臺實現的,在自定義運行時中則開放給用戶。本階段可以做許多初始化的工作,例如上文提到的加載擴展、啟動自定義插件、創建連接池等。初始化完成后,可以通過/runtime/init/ready的API通知平臺初始化已經完成。

最后進入函數調用階段,通過/runtime/invocation/next API中長輪詢的方式獲取時間,并調用函數處理對應事件。處理完畢后,通過/runtime/invocation/response | error等API推送函數的處理結果或者異常報告。由此可見,在函數調用和初始化階段,都可以通過用戶自定義和平臺的運行時API進行交互,定義函數的生命周期。

通過自定義運行時的能力,FaaS函數平臺可以完美支持Deno、.NET、WASM、Swift等編程語言,同時也能滿足用戶對運行時自定義擴展和插件的需求,進一步擴展FaaS函數的邊界。

3.6.2 自定義運行時示例

接下來,我們通過一個Deno環境的函數示例,展示FaaS平臺自定義運行時的運行原理。Deno由Node.js的創始人Ryan Dahl在2017年創立。和Node.js一樣,Deno也是一個服務器運行時,它支持多種語言,可以直接運行JavaScript和TypeScript代碼。對比Node.js,Deno提供了安全的執行環境,并且能夠開箱即用地支持TypeScript等開發語言,在開發者中也越來越受歡迎。但當前各大云廠商的FaaS平臺默認僅支持Node.js運行時,因此在本節,我們通過自定義運行時創建Deno環境。

在創建自定義運行時Custom Runtime函數前,首先需要創建運行時引導文件bootstrap和函數處理文件。其中,bootstrap是運行時入庫引導程序文件。創建bootstrap文件,定義Deno的文件目錄并執行入口文件entry.js,如代碼清單3-2所示。

代碼清單3-2 Deno環境下創建bootstrap文件

# 設置Deno緩存目錄
export DENO_DIR=/tmp
# 取消代理
unset http_proxy
unset https_proxy
# 執行入口文件
deno run --allow-net --allow-env entry.js

入口文件創建完畢后,接下來需要創建函數處理文件。函數處理文件主要包含函數邏輯的具體實現,其執行方式及參數可以通過運行時自定義實現。在本例中,創建entry.ts作為入口文件,在其中創建HTTP Server、監聽固定端口、進行函數的初始化等,如代碼清單3-3所示。

代碼清單3-3 Deno環境的函數處理文件

const SCF_RUNTIME_API: string | undefined = Deno.env.get('SCF_RUNTIME_API');
const SCF_RUNTIME_API_PORT: string | undefined = Deno.env.get(
    'SCF_RUNTIME_API_PORT',
);

const READY_URL = 'http://${SCF_RUNTIME_API}:${SCF_RUNTIME_API_PORT}/runtime/init/ready';
const EVENT_URL = 'http://${SCF_RUNTIME_API}:${SCF_RUNTIME_API_PORT}/runtime/invocation/next';
const RESPONSE_URL = 'http://${SCF_RUNTIME_API}:${SCF_RUNTIME_API_PORT}/runtime/invocation/response';
const ERROR_URL = 'http://${SCF_RUNTIME_API}:${SCF_RUNTIME_API_PORT}/runtime/invocation/error';

import { app } from './src/main.jsx';

const PORT = 9000;

async function post(url = '', data = {}) {
    const response = await fetch(url, {
        method: 'POST', // 默認為GET, 也支持POST、PUT、DELETE等請求方式
        body: JSON.stringify(data),
    });
    return response.text();
}

async function forwardEventToRequest(event: any) {
    // 獲取請求的事件
    console.log(
        '++++++++ Req Url +++++++',
        'http://localhost:${PORT}/${event.path}',
    );

    const response = await fetch('http://localhost:${PORT}/${event.path}', {
        method: 'GET',
    });
    const body = await response.text();

    const apigwReturn = {
        statusCode: 200,
        body: body,
        headers: {
            'Content-Type': 'text/html; charset=UTF-8',
        },
        isBase64Encoded: false,
    };

    return apigwReturn;
}

async function run() {
    // 事件循環
    // 獲取事件
    const eventObj: any = await fetch(EVENT_URL);
    const event = await eventObj.json();
    await app.start({ port: PORT });
    const apigwReturn = await forwardEventToRequest(event);
    await app.close();
    if (!event) {
        const error = await post(ERROR_URL, { msg: 'error handling event' });
        console.log('Error response: ${error}');
    } else {
        console.log('Send Invoke Response: ${event}');
        await post(RESPONSE_URL, apigwReturn);
    }
}
// 完成 POST 請求,初始化完畢
post(READY_URL, { msg: 'deno ready' }).then(() => {
    console.log('Initialize finish');
    run();
});

示例完整代碼地址為https://github.com/serverless-plus/serverless-deno。

主站蜘蛛池模板: 铁力市| 乌鲁木齐市| 苍溪县| 阳谷县| 宜兰市| 茂名市| 广西| 衡南县| 涿鹿县| 开远市| 获嘉县| 日喀则市| 康平县| 昌乐县| 南安市| 星座| 马边| 兴安县| 赤峰市| 洛宁县| 肥东县| 石泉县| 博客| 潞城市| 林周县| 织金县| 石泉县| 札达县| 贡嘎县| 乌审旗| 海淀区| 木里| 富源县| 馆陶县| 吉安县| 太仆寺旗| 白城市| 榆树市| 沙湾县| 七台河市| 芦山县|