您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關(guān)如何將 Web 框架遷移到 Serverless,小編覺得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
Serverless 通常翻譯為「無服務(wù)架構(gòu)」,是一種軟件系統(tǒng)設(shè)計架構(gòu)思想和方法,并不是一個開發(fā)框架或者工具。他的出現(xiàn)是為了讓開發(fā)者更加關(guān)注業(yè)務(wù)的開發(fā),而將繁雜的運(yùn)維和部署交給云廠商。Serverless 由 Faas 和 Baas 組成,F(xiàn)aas 為開發(fā)者提供業(yè)務(wù)運(yùn)算環(huán)境,然后與 Baas 提供的數(shù)據(jù)和存儲服務(wù),進(jìn)行交互,從而提供與傳統(tǒng)服務(wù)一致的體驗。但是由于 Faas 是無狀態(tài)的,并且其運(yùn)行環(huán)境是有讀寫限制的,最重要的是它是基于事件觸發(fā)的。因此如果傳統(tǒng) Web 服務(wù)想遷移到 Serverless 上,是需要進(jìn)行相關(guān)改造和特殊處理的,為此遷移成本是必不可少的。下面將具體幫助大家剖析下,如何 Serverless 化傳統(tǒng)的 Web 服務(wù)。
Web 服務(wù)定義:
Web 服務(wù)是一種
面向服務(wù)的架構(gòu)
(SOA) 的技術(shù),通過標(biāo)準(zhǔn)的 Web 協(xié)議提供服務(wù),目的是保證不同平臺的應(yīng)用服務(wù)可以互操作。
日常生活中,接觸最多的就是基于 HTTP 協(xié)議的服務(wù),客戶端發(fā)起請求,服務(wù)端接受請求,進(jìn)行計算處理,然后返回響應(yīng),簡單示意圖如下:
傳統(tǒng) Web 服務(wù)部署流程:通常需要將項目代碼部署到服務(wù)器上,啟動服務(wù)進(jìn)程,監(jiān)聽服務(wù)器的相關(guān)端口,然后等待客戶端請求,從而響應(yīng)返回處理結(jié)果。而這個服務(wù)進(jìn)程是常駐的,就算沒有客戶端請求,也會占用相應(yīng)服務(wù)器資源。
一般我們的服務(wù)是由高流量和低流量場景交替組成的,但是為了考慮高流量場景,我們需要提供較高的服務(wù)器配置和多臺服務(wù)進(jìn)行負(fù)載均衡。這就導(dǎo)致服務(wù)處在低流量場景時,會多出很多額外的閑置資源,但是購買的資源卻需要按照高流量場景進(jìn)行付費(fèi),這是非常不劃算的。
如果我們的服務(wù)能在高流量場景自動擴(kuò)容,低流量場景自動縮容,并且只在進(jìn)行計算處理響應(yīng)時,才進(jìn)行收費(fèi),而空閑時間不占用任何資源,就不需要收費(fèi)呢?
答案就是 Serverless
。
上面已經(jīng)提到了 Serverless 的兩個核心特點(diǎn):按需使用和收費(fèi)
和 自動擴(kuò)縮容
。而且近幾年 Serverless 的應(yīng)用也越來越廣泛,但是它并不是銀彈,任何技術(shù)都是有它的適合場景和不適合場景。我們不能因為一項技術(shù)的火熱,而盲目的追捧。Serverless 是有它的局限性的,一般 Serverless 適合如下幾種場景:
異步的并發(fā),組件可獨(dú)立部署和擴(kuò)展
應(yīng)對突發(fā)或服務(wù)使用量不可預(yù)測
無狀態(tài),計算耗時較短服務(wù)
請求延時不敏感服務(wù)
需要快速開發(fā)迭代的業(yè)務(wù)
如果你的服務(wù)不滿足以上條件,筆者是不推薦遷移到 Serverless。
如果你的服務(wù)是以上提到的任何話一個場景,那么就可以嘗試遷移到 Serverless 上。
常見的 Serverless HTTP 服務(wù)結(jié)構(gòu)圖如下:
那么我們?nèi)绾螌?Web 服務(wù)進(jìn)行遷移呢?
我們知道 Faas (云函數(shù))是基于事件觸發(fā)的,也就是云函數(shù)被觸發(fā)運(yùn)行時,接收到的是一個 JSON 結(jié)構(gòu)體
,它跟傳統(tǒng) Web 請求時有區(qū)別的,這就是為什么需要額外的改造工作。而改造的工作就是圍繞如何將事件 JSON 結(jié)構(gòu)體轉(zhuǎn)化成標(biāo)準(zhǔn)的 Web 請求
。
所以 Serverless 化 Web 服務(wù)的核心就是需要開發(fā)一個 適配層
,來幫我們將觸發(fā)事件轉(zhuǎn)化為標(biāo)準(zhǔn)的 Web 請求。
整個處理流程圖如下:
接下來將介紹如何為 Express 框架開發(fā)一個適配層。
首先我們先來看看一個標(biāo)準(zhǔn)的云函數(shù)結(jié)構(gòu):
module.exports.handler = (event, context) => { // do some culculation return res; };
在介紹如何開發(fā)一個 Express 的適配層前,我們先來熟悉下 Express 框架。
一個簡單的 Node.js Web 服務(wù)如下:
const http = require("http"); const server = http.createServer(function (req, res) { res.end("helloword"); }); server.listen(3000);
Express 就是基于 Node.js 的 Web 框架,而 Express 核心就是 通過中間件的方式,生成一個回調(diào)函數(shù)
,然后提供給 http.createServer()
方法使用。
Express 核心架構(gòu)圖如下:
由此可知,我們可以將 Express 框架生成的回調(diào)函數(shù),作為 http.createServer()
的參數(shù),來創(chuàng)建可控的 HTTP Server,然后將云函數(shù)的 event
對象轉(zhuǎn)化成一個 request
對象,通過 http.request()
方法發(fā)起 HTTP 請求,獲取請求響應(yīng),返回給用戶,就可以實(shí)現(xiàn)我們想要的結(jié)果。
對于 Node.js 的 HTTP Server,可以通過調(diào)用 server.listen()
方法來啟動服務(wù),listen()
方法支持多種參數(shù)類型,主要有兩種監(jiān)聽方式 從一個TCP端口啟動監(jiān)聽
和 從一個UNIX Socket套接字啟動監(jiān)聽
。
server.listen(port[, hostname][, backlog][, callback])
:從一個TCP端口啟動監(jiān)聽
server.listen(path, [callback])
:從一個UNIX Domain Socket啟動監(jiān)聽
服務(wù)器創(chuàng)建后,我們可以像下面這樣啟動服務(wù)器:
// 從'127.0.0.1'和3000端口開始接收連接 server.listen(3000, '127.0.0.1', () => {}); // 從 UNIX 套接字所在路徑 path 上監(jiān)聽連接 server.listen('path/to/socket', () => {})
無論是 TCP Socket
還是 Unix Domain Socket
,每個 Socket 都是唯一的。TCP Socket
通過 IP和端口
描述,而 Unix Domain Socket
通過 文件路徑
描述。
TCP 屬于傳輸層的協(xié)議,使用 TCP Socket 進(jìn)行通訊時,需要經(jīng)過傳輸層 TCP/IP 協(xié)議的解析。
而 Unix Domain Socket
可用于不同進(jìn)程間的通訊和傳遞,使用 Unix Domain Socket
進(jìn)行通訊時不需要經(jīng)過傳輸層,也不需要使用 TCP/IP 協(xié)議
。所以,理論上講 Unix Domain Socket
具有更好的傳輸效率。
因此這里在設(shè)計啟動服務(wù)時,采用了 Unix Domain Socket
方式,以便減少函數(shù)執(zhí)行時間,節(jié)約成本。
關(guān)于 Node.js 如何實(shí)現(xiàn) IPC 通信,這里就不詳細(xì)介紹的,感興趣的小伙伴可以深入研究下,這里有個簡單的示例,nodejs-ipc
原理大概介紹清楚了,我們的核心實(shí)現(xiàn)代碼需要以下三步:
通過 Node.js HTTP Server 監(jiān)聽 Unix Domain Socket,啟動服務(wù)
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; }
將 Serverless Event 對象轉(zhuǎn)化為 Http 請求
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; } }
將 HTTP 響應(yīng)轉(zhuǎn)化為 API 網(wǎng)關(guān)標(biāo)準(zhǔn)數(shù)據(jù)結(jié)構(gòu)
function forwardResponseToApiGateway(server, response, resolver) { response .on("data", (chunk) => buf.push(chunk)) .on("end", () => { // ... resolver.succeed({ statusCode, body, headers, isBase64Encoded, }); }); }
最后函數(shù)的 handler 將異步請求返回就可以了。
如果不想手寫這些適配層代碼,可以直接使用 tencent-serverless-http 模塊。
它使用起來很簡單,創(chuàng)建我們的 Express 應(yīng)用入口文件 sls.js
:
const express = require("express"); const app = express(); // Routes app.get(`/`, (req, res) => { res.send({ msg: `Hello Express`, }); }); module.exports = app;
然后創(chuàng)建云函數(shù) 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; };
接下來,將業(yè)務(wù)代碼和依賴模塊一起打包部署到云函數(shù)就可以了(記得指定 執(zhí)行方法
為 sl_handler.handler
)。
除了 Express
框架,其他的 Node.js 框架也基本類似,只需要按照要求,exports
一個 HTTP Server
的回調(diào)函數(shù)就可以。
比如 Koa
,我們拿到初始化的 Koa
應(yīng)用后,只需要將 app.callback()
作為 createServer()
方法的參數(shù)就可以了,如下:
const { createServer, proxy } = require("tencent-serverless-http"); const app = require("./app"); exports.handler = async (event, context) => { // 這里和 Express 略有區(qū)別 const server = createServer(app.callback()); const result = await proxy(server, event, context, "PROMISE").promise; };
對于非 Node.js 框架,比如 Python
的 Flask
框架,原理都是一樣的,核心只需要做到 將 Serverless Event 對象轉(zhuǎn)化為 Http 請求
,就可以了。由于筆者對其他語言不太熟悉,這里就不做深入介紹了,感興趣的小伙伴,可以到 Github 社區(qū)搜索下,已經(jīng)有很多對應(yīng)的解決方案了,或者自己嘗試手?jǐn)]也是可以的。
讀到這里,相信你已經(jīng)清楚,如何將自己的 Node.js 框架遷移到 Serverless 了。但是在這之前,我們都是手動處理的,而且每次都需要自己創(chuàng)建 handler.js
文件,還是不夠方便。
為此開源社區(qū)提供了一套優(yōu)秀的解決方案 Serverless Component,通過組件,我們進(jìn)行簡單的 yaml
文件配置后,就可以方便的將我們的框架代碼部署到云端。
比如上面提到的 Express
框架,就有對應(yīng)的組件,我們只需要在項目根目錄下創(chuàng)建 serverless.yml
配置文件:
component: express name: expressDemo inputs: src: ./ region: ap-guangzhou runtime: Nodejs10.15 apigatewayConf: protocols: - https environment: release
然后全局安裝 serverless
命令 npm install serverless -g
之后,執(zhí)行部署命令即可:
$ serverless deploy
耐心等待幾秒,我們的 Express 應(yīng)用就成功部署到云端了。
注意:本文 Serverless 服務(wù)均基于 騰訊云
部署。
Serverless Express 組件不僅能幫我們快速部署 Express 應(yīng)用,而且它還提供了 實(shí)時日志
和 云端調(diào)試
的能力。
只需要在項目目錄下執(zhí)行 serverless dev
命令,serverless 命令行工具就會自動監(jiān)聽項目業(yè)務(wù)代碼的更改,并且實(shí)時部署,同時我們可以通過打開 Chrome Devtools 來調(diào)試 Express 應(yīng)用。
以上就是如何將 Web 框架遷移到 Serverless,小編相信有部分知識點(diǎn)可能是我們?nèi)粘9ぷ鲿姷交蛴玫降?。希望你能通過這篇文章學(xué)到更多知識。更多詳情敬請關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。