Serverless 一般翻譯爲「無服務架構」,是一種軟件系統設計架構思想和方法,並非一個開發框架或者工具。他的出現是爲了讓開發者更加關注業務的開發,而將繁雜的運維和部署交給雲廠商。Serverless 由 Faas 和 Baas 組成,Faas 爲開發者提供業務運算環境,而後與 Baas 提供的數據和存儲服務,進行交互,從而提供與傳統服務一致的體驗。可是因爲 Faas 是無狀態的,而且其運行環境是有讀寫限制的,最重要的是它是基於事件觸發的。所以若是傳統 Web 服務想遷移到 Serverless 上,是須要進行相關改造和特殊處理的,爲此遷移成本是必不可少的。本文將具體幫助你們剖析下,如何 Serverless 化傳統的 Web 服務。html
讀完本文將瞭解到:node
Web 服務定義:git
Web 服務是一種
面向服務的架構
(SOA) 的技術,經過標準的 Web 協議提供服務,目的是保證不一樣平臺的應用服務能夠互操做。
平常生活中,接觸最多的就是基於 HTTP 協議的服務,客戶端發起請求,服務端接受請求,進行計算處理,而後返回響應,簡單示意圖以下:github
傳統 Web 服務部署流程:一般須要將項目代碼部署到服務器上,啓動服務進程,監聽服務器的相關端口,而後等待客戶端請求,從而響應返回處理結果。而這個服務進程是常駐的,就算沒有客戶端請求,也會佔用相應服務器資源。web
通常咱們的服務是由高流量和低流量場景交替組成的,可是爲了考慮高流量場景,咱們須要提供較高的服務器配置和多臺服務進行負載均衡。這就致使服務處在低流量場景時,會多出不少額外的閒置資源,可是購買的資源卻須要按照高流量場景進行付費,這是很是不划算的。express
若是咱們的服務能在高流量場景自動擴容,低流量場景自動縮容,而且只在進行計算處理響應時,才進行收費,而空閒時間不佔用任何資源,就不須要收費呢?npm
答案就是 Serverless
。api
上面已經提到了 Serverless 的兩個核心特色:按需使用和收費
和 自動擴縮容
。並且近幾年 Serverless 的應用也愈來愈普遍,可是它並非銀彈,任何技術都是有它的適合場景和不適合場景。咱們不能由於一項技術的火熱,而盲目的追捧。Serverless 是有它的侷限性的,通常 Serverless 適合以下幾種場景:promise
若是你的服務不知足以上條件,筆者是不推薦遷移到 Serverless。瀏覽器
若是你的服務是以上提到的任何話一個場景,那麼就能夠嘗試遷移到 Serverless 上。
常見的 Serverless HTTP 服務結構圖以下:
那麼咱們如何將 Web 服務進行遷移呢?
咱們知道 Faas (雲函數)是基於事件觸發的,也就是雲函數被觸發運行時,接收到的是一個 JSON 結構體
,它跟傳統 Web 請求時有區別的,這就是爲何須要額外的改造工做。而改造的工做就是圍繞如何將事件 JSON 結構體轉化成標準的 Web 請求
。
因此 Serverless 化 Web 服務的核心就是須要開發一個 適配層
,來幫咱們將觸發事件轉化爲標準的 Web 請求。
整個處理流程圖以下:
接下來將介紹如何爲 Express 框架開發一個適配層。
首先咱們先來看看一個標準的雲函數結構:
module.exports.handler = (event, context) => { // do some culculation return res; };
在介紹如何開發一個 Express 的適配層前,咱們先來熟悉下 Express 框架。
一個簡單的 Node.js Web 服務以下:
const http = require("http"); const server = http.createServer(function (req, res) { res.end("helloword"); }); server.listen(3000);
Express 就是基於 Node.js 的 Web 框架,而 Express 核心就是 經過中間件的方式,生成一個回調函數
,而後提供給 http.createServer()
方法使用。
Express 核心架構圖以下:
由此可知,咱們能夠將 Express 框架生成的回調函數,做爲 http.createServer()
的參數,來建立可控的 HTTP Server,而後將雲函數的 event
對象轉化成一個 request
對象,經過 http.request()
方法發起 HTTP 請求,獲取請求響應,返回給用戶,就能夠實現咱們想要的結果。
對於 Node.js 的 HTTP Server,能夠經過調用 server.listen()
方法來啓動服務,listen()
方法支持多種參數類型,主要有兩種監聽方式 從一個TCP端口啓動監聽
和 從一個UNIX Socket套接字啓動監聽
。
server.listen(port[, hostname][, backlog][, callback])
:從一個TCP端口啓動監聽server.listen(path, [callback])
:從一個UNIX Domain Socket啓動監聽
服務器建立後,咱們能夠像下面這樣啓動服務器:
// 從'127.0.0.1'和3000端口開始接收鏈接 server.listen(3000, '127.0.0.1', () => {}); // 從 UNIX 套接字所在路徑 path 上監聽鏈接 server.listen('path/to/socket', () => {})
不管是 TCP Socket
仍是 Unix Domain Socket
,每一個 Socket 都是惟一的。TCP Socket
經過 IP和端口
描述,而 Unix Domain Socket
經過 文件路徑
描述。
TCP 屬於傳輸層的協議,使用 TCP Socket 進行通信時,須要通過傳輸層 TCP/IP 協議的解析。
而 Unix Domain Socket
可用於不一樣進程間的通信和傳遞,使用 Unix Domain Socket
進行通信時不須要通過傳輸層,也不須要使用 TCP/IP 協議
。因此,理論上講 Unix Domain Socket
具備更好的傳輸效率。
所以這裏在設計啓動服務時,採用了 Unix Domain Socket
方式,以便減小函數執行時間,節約成本。
關於 Node.js 如何實現 IPC 通訊,這裏就不詳細介紹的,感興趣的小夥伴能夠深刻研究下,這裏有個簡單的示例, nodejs-ipc
原理大概介紹清楚了,咱們的核心實現代碼須要如下三步:
function createServer(requestListener, serverListenCallback) { const server = http.createServer(requestListener); server._socketPathSuffix = getRandomString(); server.on("listening", () => { server._isListening = true; if (serverListenCallback) serverListenCallback(); }); server .on("close", () => { server._isListening = false; }) .on("error", (error) => { // ... }); server.listen(`/tmp/server-${server._socketPathSuffix}.sock`) return server; }
function forwardRequestToNodeServer(server, event, context, resolver) { try { const requestOptions = mapApiGatewayEventToHttpRequest( event, context, getSocketPath(server._socketPathSuffix), ); // make http request to node server const req = http.request(requestOptions, (response) => forwardResponseToApiGateway(server, response, resolver), ); if (event.body) { const body = getEventBody(event); req.write(body); } req .on('error', (error) => // ... ) .end(); } catch (error) { // ... return server; } }
function forwardResponseToApiGateway(server, response, resolver) { response .on("data", (chunk) => buf.push(chunk)) .on("end", () => { // ... resolver.succeed({ statusCode, body, headers, isBase64Encoded, }); }); }
最後函數的 handler 將異步請求返回就能夠了。
若是不想手寫這些適配層代碼,能夠直接使用 tencent-serverless-http 模塊。
它使用起來很簡單,建立咱們的 Express 應用入口文件 sls.js
:
const express = require("express"); const app = express(); // Routes app.get(`/`, (req, res) => { res.send({ msg: `Hello Express`, }); }); module.exports = app;
而後建立雲函數 sl_handler.js
文件:
const { createServer, proxy } = require("tencent-serverless-http"); const app = require("./app"); exports.handler = async (event, context) => { const server = createServer(app); const result = await proxy(server, event, context, "PROMISE").promise; };
接下來,將業務代碼和依賴模塊一塊兒打包部署到雲函數就能夠了(記得指定 執行方法
爲 sl_handler.handler
)。
除了 Express
框架,其餘的 Node.js 框架也基本相似,只須要按照要求,exports
一個 HTTP Server
的回調函數就能夠。
好比 Koa
,咱們拿到初始化的 Koa
應用後,只須要將 app.callback()
做爲 createServer()
方法的參數就能夠了,以下:
const { createServer, proxy } = require("tencent-serverless-http"); const app = require("./app"); exports.handler = async (event, context) => { // 這裏和 Express 略有區別 const server = createServer(app.callback()); const result = await proxy(server, event, context, "PROMISE").promise; };
對於非 Node.js 框架,好比 Python
的 Flask
框架,原理都是同樣的,核心只須要作到 將 Serverless Event 對象轉化爲 Http 請求
,就能夠了。因爲筆者對其餘語言不太熟悉,這裏就不作深刻介紹了,感興趣的小夥伴,能夠到 Github 社區搜索下,已經有不少對應的解決方案了,或者本身嘗試手擼也是能夠的。
讀到這裏,相信你已經清楚,如何將本身的 Node.js 框架遷移到 Serverless 了。可是在這以前,咱們都是手動處理的,並且每次都須要本身建立 handler.js
文件,仍是不夠方便。
爲此開源社區提供了一套優秀的解決方案 Serverless Component,經過組件,咱們進行簡單的 yaml
文件配置後,就能夠方便的將咱們的框架代碼部署到雲端。
好比上面提到的 Express
框架,就有對應的組件,咱們只須要在項目根目錄下建立 serverless.yml
配置文件:
component: express name: expressDemo inputs: src: ./ region: ap-guangzhou runtime: Nodejs10.15 apigatewayConf: protocols: - https environment: release
而後全局安裝 serverless
命令 npm install serverless -g
以後,執行部署命令便可:
$ serverless deploy
耐心等待幾秒,咱們的 Express 應用就成功部署到雲端了。更多詳細信息,請參考 Express 官方文檔
注意:本文 Serverless 服務均基於
騰訊雲
部署。
Serverless Express 組件不只能幫咱們快速部署 Express 應用,並且它還提供了 實時日誌
和 雲端調試
的能力。
只須要在項目目錄下執行 serverless dev
命令,serverless 命令行工具就會自動監聽項目業務代碼的更改,而且實時部署,同時咱們能夠經過打開 Chrome Devtools 來調試 Express 應用。
關於雲端調試, 騰訊雲 Serverless Framework 正式發佈公告 中有詳細的介紹,而且有視頻演示。
並且除了 Express 組件,還支持: Koa.js,Egg.js,Next.js,Nuxt.js.....
固然 Serverless 化 Web 服務並無本文介紹的那麼簡單,好比文件讀寫,服務日誌存儲,Cookie/Session
存儲等......實際開發中,咱們還會面臨各類未知的坑,可是比起困難,Serverless 帶給咱們的收益是值得去嘗試的。固然傳統 Web 服務真的適合遷移到 Serverless 架構上,也是值得咱們去思考的問題,畢竟現有的 Web 框架都是面向傳統 Web 服務開發實現的 (推薦閱讀 利與弊-傳統框架要不要部署在 Serverless 架構上)。可是筆者相信,很快就會出現一個專門爲 Serverless 而生的 Web 框架,能夠幫助咱們更好地基於 Serverless 開發應用 ~
3 秒你能作什麼?喝一口水,看一封郵件,仍是 —— 部署一個完整的 Serverless 應用?
複製連接至 PC 瀏覽器訪問: https://serverless.cloud.tenc...
3 秒極速部署,當即體驗史上最快的 Serverless HTTP 實戰開發!
傳送門:
- GitHub: github.com/serverless
- 官網:serverless.com
歡迎訪問:Serverless 中文網,您能夠在 最佳實踐 裏體驗更多關於 Serverless 應用的開發!
推薦閱讀: 《Serverless 架構:從原理、設計到項目實戰》