結合阿里雲 FC 談談我對 FaaS 的理解

這是第 76 篇不摻水的原創,想獲取更多原創好文,請搜索公衆號關注咱們吧~ 本文首發於政採雲前端博客: 結合阿里雲 FC 談談我對 FaaS 的理解

進入主題以前,先從背景簡述下最近前端界的熱點詞彙 Serverless,其實,Serverless 這個概念在其餘領域已經提出來好久了。javascript

Serverless

概念

Serverless 直譯爲無服務器,表明一種無服務器架構(也被稱爲「無服務器計算」),並不表示不須要物理服務器,而是指不須要關注和管理服務器,直接使用服務便可,其實就是一種服務外包或者說服務託管,這些服務由第三方供應商提供。css

具體來講,Serverless 就是指服務端邏輯由開發者實現,運行在無狀態的計算容器中,由事件驅動,徹底被第三方管理,而業務層面的狀態則記錄在數據庫或存儲資源中。html

目前國內一些大型公司如阿里、騰訊、滴滴都已經將 Serverless 逐步在業務中落地(案例分享:2020.06.19 ServerlessDays · China Online)。前端

Serverless 與 FaaS 的聯繫

Serverless = FaaS+BaaS ,是目前界內比較公認的定義:vue

  • FaaS(Function as a Service):函數即服務java

    • 負責服務端業務邏輯場景,須要開發者本身實現,服務的粒度比微服務更小,小到以函數爲單位
    • 事件驅動的 Serverless 服務,毫秒級彈性伸縮
    • 沒法進行內存或數據共享,是無狀態的
    • 無需運維,部署、運維等都由雲平臺提供
  • BaaS(Backend as Service):後端即服務node

    • 負責通用服務場景,不須要開發者本身開發,由雲廠商提供,好比數據庫、身份驗證、消息隊列、對象存儲等服務
    • 有狀態

筆者認爲,單看 FaaS 或者 BaaS,都是一種 Serverless ,只是通常咱們完整的應用須要二者結合才能實現。nginx

好,下面正式介紹 FaaS。git

FaaS

做爲一個前端,咱們平日裏很難接觸到服務器、運維方面的操做。假設如今給你一個任務,讓你本身開發一個有先後端交互的應用,並從 0 到 1 進行部署,是否是以爲光靠本身根本搞不定,這個任務有點難。github

傳統應用的部署,咱們須要作不少工做:準備服務器、配置環境、購買域名、配置 nginx、……。應用發佈以後,咱們還要考慮運維的問題,線上監控,擴縮容,容災等等等等。

如今,咱們運用 FaaS 去開發部署的話,以上都不用考慮,只須要專一於業務邏輯開發便可,由於其它一切都託管給 FaaS 平臺幫咱們處理了。

概念

FaaS 是無服務器計算的一種形式。經過 FaaS,能夠快速構建任何類型的應用和服務,它具備開發敏捷特性、自動彈性伸縮能力、免運維和完善的監控設施。所以:

  • 開發者只需專一於業務邏輯開發,無需管理服務器、運行環境等 IT 基礎設施
  • FaaS 平臺會爲咱們準備好計算資源,以 彈性、可靠的方式運行咱們的代碼,實現毫秒級彈性伸縮,輕鬆應對峯值壓力
  • 用戶只需根據函數的實際執行時間按量付費

所以,相比傳統開發模式,FaaS 大大提升了開發和交付效率,是將來雲服務發展的大趨勢。自 2014 年始,在 AWS Lambda 以後,Google、IBM、Microsoft、阿里、騰訊、華爲等國內外雲廠商相繼推出雲函數計算平臺 FaaS。

快速建立 FaaS 應用

函數計算開發方式有不少種,好比:Fun 工具、函數計算 FC 平臺、Serverless VScode、雲開發平臺。本文藉助阿里雲函數計算平臺,經過其提供的模版快速建立、部署一個 Web 應用,向你們更清楚地展現 FaaS 是什麼。

本文直接基於模版建立,用戶也能夠選擇本身上傳應用代碼直接部署 Web 應用。

咱們選擇有先後端交互、數據增刪改查等行爲的 Todo List 應用,它是一個先後端一體化(先後端代碼共屬一個項目中開發、調試、部署,高效且節省資源) FaaS 應用。

服務/函數

服務

一個應用能夠拆分爲多個服務。從資源使用維度出發,一個服務能夠由多個函數組成。先建立服務,再建立函數。

能夠看到 TodoList 應用部署成功後建立好的服務,咱們能夠對該服務進行配置、刪除、查看指標等操做,還能夠對其下的函數進行配置、刪除。

函數

函數是系統調度和運行的單位。函數必須從屬於服務,同一個服務下能夠有多個函數,同一個服務下的全部函數共享相同的設置,例如服務受權、日誌配置,但彼此相互獨立,互不影響。

函數代碼

FaaS 對多種語言都有良好的支持性,好比阿里雲支持 Node.js、Python、PHP、Java 等等,開發者可使用本身熟悉的語言,根據平臺提供的函數接口形式編寫代碼。這也意味着,團隊協做時,你們能夠利用多種語言混合開發複雜應用。

點擊代碼執行,能夠看到這裏有一個在線編輯器,裏面就是模板生成的代碼,能夠在此處進行運行、調試。該應用前端頁面用 React 實現,後端服務基於 Node 的 Express 框架。

函數入口

template.yml是咱們的函數信息配置文件,告訴雲廠商咱們的代碼入口函數、觸發器類型等操做。

函數入口爲src/index.handler ,即src/index.js服務端代碼文件中的 handler 方法,該文件代碼以下:

const { Server } = require('@webserverless/fc-express')
const express = require('express');
const fs = require('fs');
const path = require('path');
const bodyParser = require('body-parser');

// initial todo list
let todos = [
  {
    id: '123',
    text: 'Go shopping',
    isCompleted: false,
  },
  {
    id: '213',
    text: 'Clean room',
    isCompleted: true,
  },
];


const staticBasePath = path.join('public', 'build');

const app = express();

// index.html
app.all("/", (req, resp) => {
  resp.setHeader('Content-Type', 'text/html');
  resp.send(fs.readFileSync('./public/build/index.html', 'utf8'));
});

// 靜態資源文件:js、css、圖片
// static js resources  
app.all('/*.js', (req, resp) => {
  const filePath = path.join(staticBasePath, req.path);
  resp.setHeader('Content-Type', 'text/javascript');
  resp.send(fs.readFileSync(filePath, 'utf8'));
});

// static css resources
app.all('/*.css', (req, resp) => {
  const filePath = path.join(staticBasePath, req.path);
  resp.setHeader('Content-Type', 'text/css');
  resp.send(fs.readFileSync(filePath, 'utf8'));
});

// static svg resources
app.all('/*.svg', (req, resp) => {
  const filePath = path.join(staticBasePath, req.path);
  resp.setHeader('Content-Type', 'image/svg+xml');
  resp.send(fs.readFileSync(filePath, 'utf8'));
});

// static png resources
app.all('/*.png', (req, resp) => {
  const filePath = path.join(staticBasePath, req.path);
  resp.setHeader('Content-Type', 'image/png');
  resp.send(fs.readFileSync(filePath, 'utf8'));
});

app.all('/manifest.json', (req, resp) => {
  const filePath = path.join(staticBasePath, req.path);
  resp.setHeader('Content-Type', 'application/json');
  resp.send(fs.readFileSync(filePath, 'utf8'));
});

// 增刪改查對應的api接口
// list api
app.get('/api/listTodos', (req, resp) => {
  resp.send(JSON.stringify(todos));
});

// create api
app.get('/api/createTodo', (req, resp) => {
  const { todo: todoStr } = req.query;
  const todo = JSON.parse(todoStr);
  todos.push({
    id: todo.id,
    text: todo.text,
    isCompleted: todo.isCompleted,
  });
  resp.send(JSON.stringify(todos));
});

// update api
app.get('/api/updateTodo', (req, resp) => {
  const { todo: todoStr } = req.query;
  const targetTodo = JSON.parse(todoStr);
  const todo = todos.find((todo) => todo.id === targetTodo.id);
  if (todo) {
    todo.isCompleted = targetTodo.isCompleted;
    todo.text = targetTodo.text;
  }
  resp.send(JSON.stringify(todos));
});

// remove api
app.get('/api/removeTodo', (req, resp) => {
  const { id } = req.query
  // TODO: Implement methods to filter todos, filtering out item with the same id
  // todos = todos.filter();
  const todosIndex = todos.findIndex((todo) => todo.id === id);
  if (todosIndex !== -1) {
    todos.splice(todosIndex, 1);
  } 
  resp.send(JSON.stringify(todos));
});

const server = new Server(app);

// 向外暴露了 http觸發器入口
// http trigger entry
module.exports.handler = function(req, res, context) {
  server.httpProxy(req, res, context);
};

能夠看到,咱們不須要本身起服務,FaaS 平臺會爲咱們管理。這個文件,有兩個重要的部分:

  • 1.函數入口 handler,也是 HTTP 觸發器入口,下文會詳細介紹
// http trigger entry
module.exports.handler = function(req, res, context) {
  server.httpProxy(req, res, context);
};
  • 2.Web Service 和 api,經過路由調用相應的服務,好比請求路徑爲"/"時,會返回 Web 應用的 html 頁面;請求"/api/*"時,調用接口返回數據

觸發器

前面說過,FaaS 是一種事件驅動的計算模型,即函數的執行是由事件驅動的,沒有事件觸發,函數就不運行。與傳統開發模式不一樣,函數不須要本身啓動一個服務去監聽數據,而是經過綁定一個(或者多個)觸發器。

觸發器就是觸發函數執行的方式,咱們須要爲函數建立指定的觸發器。

FaaS 應用的觸發器有多種(不一樣雲廠商的觸發器會有所區別),但基本都支持 HTTP、對象存儲、定時任務、消息隊列等觸發器,其中 HTTP 觸發器是最多見的。

以阿里雲函數計算爲例,介紹幾個表明類型:

觸發器類型

名稱 描述
HTTP 觸發器 1.HTTP 觸發器經過發送 HTTP 請求觸發函數執行,主要適用於快速構建 Web 服務等場
2.HTTP 觸發器支持 HEAD、POST、PUT、GET 和 DELETE 方式觸發函數
3.能夠經過綁定自定義域名爲 HTTP 函數映射不一樣的 HTTP 訪問路徑
4.開發人員能夠快速使用 HTTP 觸發器搭建 Web service和 API
OSS 觸發器(對象存儲) 1.OSS 事件能觸發相關函數執行,實現對 OSS 中的數據進行自定義處理
日誌服務觸發器 1.當日志服務定時獲取更新的數據時,經過日誌服務觸發器,觸發函數消費增量的日誌數據,並完成對數據的自定義加工
定時觸發器 1.在指定的時間點自動觸發函數執行
API 網關觸發器 1.API 網關做爲事件源,當有請求到達後端服務設置爲函數計算的 API 網關時,API 網關會觸發函數的執行。函數計算會將執行結果返回給 API 網關
2.與 HTTP 觸發器相似,可應用於搭建 Web 應用。相較於 HTTP 觸發器,您可使用 API 網關進行 IP 白名單或黑名單設置等高級操做

開發者在調試時,若是不配置觸發器,也可使用控制檯、命令行工具 或者 SDK 等方式直接調用函數執行。

咱們點開 TodoList 的觸發器,能夠看到建立的 HTTP 觸發器,WEB 用戶經過 HTTP 請求便可觸發函數的執行。

注意這裏的提示語:打開連接,會下載一個 html 附件,這時咱們打開,由於找不到靜態資源文件,應用不能正常運行。

能夠點擊自定義域名去用平臺爲咱們建立的臨時域名打開:

能夠看到,頁面和功能都正常了。咱們查看、新增、更新、刪除,在控制檯裏能夠看到發起的請求和返回結果

HTTP 觸發器

該應用 HTTP 觸發器的入口函數形式以下:

// http trigger entry
module.exports.handler = function(req, res, context) {
  server.httpProxy(req, res, context);
};

配置 HTTP 觸發器的函數能夠經過 HTTP 請求被觸發執行。此時函數能夠看作一個 Web Server,對 HTTP 請求進行處理,並將處理結果返回給調用端

訪問 html 頁面、請求靜態資源文件,以及請求接口,都是經過 http 請求去觸發相應函數的執行。

能夠在這裏進行調試:

FaaS 框架

目前市面上已經有一些較爲成熟的開源 FaaS 框架,好比 OpenFaaS、funktion、Kubeless、Fission等等,本文向你們介紹阿里雲今年正式發佈的Midway FaaS框架,它是用於構建 Node.js 雲函數的 Serverless 框架,它提供了函數的本地開發、調用、測試整個鏈路。它能夠開發新的 Serveless 應用,也提供方案將傳統應用遷移至各雲廠商的雲函數。阿里內部已經使用一年多了。

咱們能夠運用腳手架@midwayjs/faas-cli提供的能力在本地快速建立、調試、mock、部署一個 FaaS 應用。Serverless 有一個很大的弊端,就是和雲服務商平臺強綁定,可是Midway Serverles 是防平臺鎖定的,它能一套代碼可以運行在不一樣的平臺和運行時之上,Midaway faas的部署能夠跨雲廠商,默認部署到阿里雲FC,咱們也能夠修改部署到其它平臺,如騰訊雲SCF、AWS Lambda。

Midway FaaS 體系也與雲工做臺進行告終合,使用了和本地一樣的能力,這裏選擇登陸阿里雲開發平臺,用示例庫模版再次建立一個 TodoList 應用進行演示,只不過這個是用 Midway FaaS 構建的。

代碼目錄結構能夠簡單抽取爲:

|-- src
|        |-- apis //函數代碼
|                |--    config //針對不一樣環境建立配置文件
|                |--    configuration.ts //擴展能力配置
|                |--    index.ts // 函數代碼入口文件,裏面包括多個函數
|        |-- components // 前端組件
|        |-- index.tsx // 前端頁面入口文件(該應用前端基於React,如果Vue,則是App.vue)
|-- f.yml // 函數信息配置文件

f.yml配置文件

service: serverless-hello-world

// 服務提供商
provider:
  name: aliyun
  runtime: nodejs10 //運行時環境及版本信息

// 函數具體信息(包括函數入口、觸發器等等)
functions:
  render:
    handler: render.handler 
    events:
      - apigw:
          path: /*
  list:
    handler: todo.list 
    events:
      - apigw:
          path: /api/list
  update:
    handler: todo.update
    events:
      - apigw:
          path: /api/update
  remove:
    handler: todo.remove
    events:
      - apigw:
          path: /api/remove
  add:
    handler: todo.add
    events:
      - apigw:
          path: /api/add
          
// 構建的配置信息
package:
  include:
    - build //打包目錄
  artifact: code.zip //打包名稱

函數代碼中一個函數對應一個 api 接口:

安裝依賴後,咱們npm run dev,阿里云爲咱們建立了一個臨時域名用於調試:

能夠看到請求的這些資源和接口數據:

一鍵部署,很是方便。

收費標準

傳統應用咱們的服務是一直佔用資源的,而 FaaS 在資源空閒時不收費,按需付費,能夠大大節省開支。

收費標準:

  • 調用函數次數
  • 函數運行時間

由於每個月都有免費額度,因此在我的平常使用時基本不須要付費。

(PS:但仍是要特別注意,部署的應用不用時必定要及時下線,不然可能會收取費用,還有開通的服務、功能也必定要仔細留意一下收費標準,好比可能想解決冷啓動的問題,會開通預留實例功能,若是咱們不用的話,必定要注意手動釋放,不然即便它沒有執行任何請求,也會從啓動開始不斷收費,費用可不小)

冷啓動

再說說 FaaS 目前備受關注的一個問題——冷啓動

FaaS 中的函數首次調用更新函數或長時間未調用時從新調用函數時,平臺會初始化一個函數實例,這個過程就是冷啓動,平均耗時在幾百毫秒。

延遲問題

FaaS 由於冷啓動,不能當即調用函數,調用延遲會給應用性能帶來影響,針對冷啓動的延遲問題,各大雲服務商很是關注,正在想辦法不斷優化。

與冷啓動相呼應的是熱啓動,熱啓動指函數調用時不用從新建立新的函數實例,而是直接複用以前的函數實例。由於 FaaS 函數若在一段時間內沒有被事件觸發運行,雲服務商就會回收運行函數的容器資源,銷燬函數實例,因此,在未被回收的這段時間內再次調用函數就是熱啓動;銷燬後,從新建立就是冷啓動。

延遲緣由

冷啓動具體作了哪些操做呢?以阿里云爲例,大體包括了調度實例、下載解壓代碼、啓動容器、啓動運行時,這一過程結束後,函數纔開始執行。因此冷啓動的啓動消耗時間受到不少因素的影響:

  • 編程語言

    有專門研究對比,不一樣語言的冷啓動時間不一樣

  • 代碼大小

​ 這個過程在冷啓動過程當中相對比較耗時,可能幾十毫秒,也可能幾秒,看代碼體積大小

  • 容器建立

    這個過程的耗時取決於雲服務商

  • 配置等

如何優化

各大雲廠商都已經有了一些優化方案的最佳實踐,須要開發者和雲廠商共同努力:

  • 減小代碼體積:

    • 開發者能夠經過精簡代碼,刪除無用依賴,加速下載函數代碼過程
    • 好比騰訊雲對代碼作了兩級的緩存,能夠有效下降下載代碼時間
  • 資源複用,縮短函數執行時間
  • 選擇冷啓動時間較少的語言
  • 選擇合適的內存:函數內存越大,冷啓動表現越優
  • 避免沒必要要的配置
  • 下降冷啓動頻率

    • 使用定時觸發器定時訪問函數,這樣能夠防止函數實例一段時間沒被使用被銷燬
    • 使用 initializer 函數入口,函數計算會異步調用初始化接口,消除初始化用戶代碼的時間
  • 預留實例

執行時長

FaaS 還有一個侷限性,就是平臺會限制函數的執行時間,超出時間後執行代碼的進程會被強行銷燬,因此 FaaS 不適合長時間運行的應用。例如 AWS Lambda 函數不容許運行超過 15 分鐘(之前只有 5 分鐘),若是超過就會中斷。使用時,應該根據本身的預期執行時間來設置超時值,防止函數的運行時間超出預期,而且建議調用函數的 client 端的 timeout 要稍稍大於函數設置的 timeout,這樣才能防止執行環境不會意外退出。

FaaS 工做流程

相信你們讀到這裏,應該差很少能夠明白 FaaS 的工做流程了,咱們總結一下:

  • 開發者編寫函數代碼,能夠在線編輯或者本地上傳,完成後,FaaS 平臺爲咱們部署應用,建立函數服務
  • 客戶端經過設置的觸發器,通知函數服務
  • 若存在函數實例,則直接在該執行環境中調用函數;沒有,則先通過冷啓動(調度實例、下載代碼、啓動實例、啓動運行時),再執行函數
  • 函數根據用戶請求量動態擴容響應請求,將內容返回給用戶。函數執行完後,若一段時間內無事件觸發,函數實例就會被銷燬,FaaS 應用快速縮容到 0

對前端的影響

Serverless 如今這麼熱,它對前端到底有什麼影響呢?

整個實踐下來發現,FaaS 幫咱們前端擴展了能力邊界,做爲前端,咱們本身一我的也能快速完成先後端開發以及部署工做,徹底不用關心服務器以及運維方面咱們不擅長的問題。前端也有機會參與服務端業務邏輯開發,更深刻業務,創造更大的價值。

結語

本文結合阿里雲 FC、Midway FaaS 框架快速建立 FaaS 應用的實踐,向你們展現了什麼是 FaaS,FaaS 的工做流程,優缺點,展示了 FaaS 顛覆傳統開發模式的魅力。如今 FaaS 的應用場景很是普遍,Web 應用及小程序等移動應用、AI 及機器學習、物聯網、實時數據處理等等。Serverless 時代,生產效率大大提升,每一個人都有更多機會創造無限可能,讓咱們一塊兒爲將來加油!

參考文章

Serverless Handbook——無服務架構實踐手冊

阿里雲函數計算使用文檔

Midway Serverless 使用文檔

騰訊雲函數計算冷啓動優化實踐

Serverless Architectures(譯文)—(Martin Fowler)

招賢納士

政採雲前端團隊(ZooTeam),一個年輕富有激情和創造力的前端團隊,隸屬於政採雲產品研發部,Base 在風景如畫的杭州。團隊現有 40 餘個前端小夥伴,平均年齡 27 歲,近 3 成是全棧工程師,妥妥的青年風暴團。成員構成既有來自於阿里、網易的「老」兵,也有浙大、中科大、杭電等校的應屆新人。團隊在平常的業務對接以外,還在物料體系、工程平臺、搭建平臺、性能體驗、雲端應用、數據分析及可視化等方向進行技術探索和實戰,推進並落地了一系列的內部技術產品,持續探索前端技術體系的新邊界。

若是你想改變一直被事折騰,但願開始能折騰事;若是你想改變一直被告誡須要多些想法,卻無從破局;若是你想改變你有能力去作成那個結果,卻不須要你;若是你想改變你想作成的事須要一個團隊去支撐,但沒你帶人的位置;若是你想改變既定的節奏,將會是「5 年工做時間 3 年工做經驗」;若是你想改變原本悟性不錯,但老是有那一層窗戶紙的模糊… 若是你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的本身。若是你但願參與到隨着業務騰飛的過程,親手推進一個有着深刻的業務理解、完善的技術體系、技術創造價值、影響力外溢的前端團隊的成長曆程,我以爲咱們該聊聊。任什麼時候間,等着你寫點什麼,發給 ZooTeam@cai-inc.com

相關文章
相關標籤/搜索