Serverless Devs 2.0 開箱測評:Serverless 開發(fā)最佳實(shí)踐

作者 | 寒斜
站點(diǎn)架構(gòu)設(shè)計(jì)

Serverless 函數(shù)代碼組織
如果想充分利用 Serverless 的能力函數(shù)是最佳方案,可以最大程度減少冷啟動時(shí)間,踐行用完即走的理念,保障用戶體驗(yàn)的同時(shí),最大程度減少成本,不過對于中大型項(xiàng)目而言,以單函數(shù)的方式組織代碼,在維護(hù)上無疑是一個(gè)巨大挑戰(zhàn),可能一個(gè)應(yīng)用會有數(shù)百個(gè)函數(shù),維護(hù)成本極高也極易出錯(cuò)。
最好的方式是用框架的方式組織代碼,以函數(shù)的方式部署執(zhí)行。框架組織代碼需要做業(yè)務(wù)的劃分,比如電商包含商品,訂單,用戶等服務(wù),都放到一個(gè)框架里面并通過函數(shù)去部署執(zhí)行的話明顯太大了。最好就是像微服務(wù)一樣,獨(dú)立業(yè)務(wù)的接口可以在同一個(gè)函數(shù)中,每一個(gè)業(yè)務(wù)有自己的獨(dú)立域名,再通過內(nèi)部路由訪問具體的業(yè)務(wù)服務(wù)。

這樣做可以最大限度的利用函數(shù)能力,并且維護(hù)得來相對容易一些。
我們把 Serverless Hub 的應(yīng)用市場作為一類場景,進(jìn)行了統(tǒng)一劃分,具體的函數(shù)調(diào)用如下實(shí)現(xiàn)(完整的代碼目錄 git)
const { http } = require('@serverless-devs/dk');const { searchApp, getAppDetail, getSpecialDetail, getSpecialApp, getCategorys, getTags } = require('./services');http.get("/appCenter/getSpecial", async (ctx) => {const data = await getSpecialApp(ctx);ctx.body = data;}).post("/appCenter/getSpecialDetail", async (ctx, next) => {const data = await getSpecialDetail(ctx);ctx.body = data;}).post("/appCenter/getAppDetail", async (ctx) => {const data = await getAppDetail(ctx);ctx.body = data;}).get("/appCenter/getCategory", async (ctx) => {const data = await getCategorys();ctx.body = data;}).get("/appCenter/getTags", async (ctx) => {const data = await getTags();ctx.body = data;}).post("/appCenter/getApps", async (ctx) => {const data = await searchApp(ctx);ctx.body = data;}).get("/", async (ctx, next) => {let result = "Hello ServerlessDevs";ctx.body = result;})http.app.use(http.routes());exports.handler = http();
代碼使用了 Serverless Devs 提供的 @serverless-devs/dk ,我們對標(biāo)準(zhǔn)的前端框架進(jìn)行了核心封裝,比如 express,koa 等,你可以繼續(xù)使用習(xí)慣的 web 框架進(jìn)行開發(fā)工作,最后通過 s 工具進(jìn)行函數(shù)部署。s.yaml 的配置如下:
edition: 1.0.0 # 命令行YAML規(guī)范版本,遵循語義化版本(Semantic Versioning)規(guī)范name: fc-http-demo # 項(xiàng)目名稱access: default # 秘鑰別名vars:services:serverlesshub:component: devsapp/fc # 組件名稱props:region: cn-hangzhouservice:name: myserverlesstestdescription: demo for fc-http componentinternetAccess: truefunction:name: myhubdescription: this is a testruntime: nodejs12codeUri: ./codehandler: index.handlermemorySize: 128timeout: 10triggers:- name: httpTriggertype: httpconfig:authType: anonymousmethods:- GET- POSTcustomDomains:- domainName: autoprotocol: HTTProuteConfigs:- path: /*# 函數(shù)計(jì)算FC組件文檔參考地址:https://github.com/devsapp/fc
如果你想再增加一個(gè)服務(wù)業(yè)務(wù),可以水平擴(kuò)展一個(gè)新的服務(wù)配置,并且可以用同樣的代碼包去實(shí)現(xiàn)。
性能優(yōu)化
通常 Serverless 應(yīng)用的最大耗時(shí)都在 冷啟動時(shí)間上,就以阿里云 FC 為例,如果我們的應(yīng)用以容器的方式進(jìn)行部署,冷啟動時(shí)間會比較長可能是 1 分鐘或者更多,即使采用鏡像加速也僅能縮短到幾十秒以內(nèi),所以如果項(xiàng)目不是特別大(AI 類應(yīng)用的包大小可能會達(dá)到 G 級別),都建議以 原生runtime 的方式去做。拿我們上面的 serverless hub 為例就是最終運(yùn)行到 nodejs12 runtime,冷啟動時(shí)間 1~2s。

熱啟動則縮短到 150ms



端云調(diào)試
調(diào)試始終是 Serverless 應(yīng)用的最棘手的部分,我們開發(fā)的應(yīng)用是在本地,部署的應(yīng)用是在線上。并且線上的環(huán)境跟本地開發(fā)環(huán)境有著巨大的差異,通常我們只能通過在線打印輸出,然后還得通過日志平臺查看日志判斷問題,再回來本地修改代碼,一來一回耗時(shí)非常巨大,而且效果也不好。Serverless Devs 提供了自己的解決方案,我們巧妙的設(shè)計(jì)了一個(gè)在線的輔助函數(shù),輔助函數(shù)完整復(fù)刻線上代碼,然后通過本地跟輔助函數(shù)建設(shè)通道實(shí)現(xiàn)本地的代碼調(diào)試效果。

打開鏈接查看更多詳細(xì)介紹:
https://github.com/devsapp/fc/blob/main/docs/Usage/proxied.md
基于這樣的能力我們再來看看具體怎么在 Serverless Hub 實(shí)施的, 演示使用的是Serverless Desktop, 大家可以嘗試跟著我的操作步驟進(jìn)行使用:
https://serverlessdevs.resume.net.cn/zh-cn/desktop/index.html
我們可以通過 Serverless Desktop 的應(yīng)用市場搜索 xxx 應(yīng)用模板,然后加載到本地,然后通過可視化配置部分修改相應(yīng)的服務(wù)和函數(shù)內(nèi)容,進(jìn)行部署。

按照提示準(zhǔn)備好前置條件,比如安裝 docker demon 指定調(diào)試端口,啟動資源準(zhǔn)備,這個(gè)時(shí)候會創(chuàng)建輔助函數(shù),同時(shí)構(gòu)建 vscode 的 debug 文件。

啟動好之后,使用 vscode 打開工程目錄,查看 debug 配置文件.vscode/launch.json

以這個(gè)啟動 debug 模式

切換到"本地調(diào)試配置"面板,點(diǎn)擊"發(fā)起調(diào)用"

可以發(fā)現(xiàn) vscode 觸發(fā)調(diào)試

調(diào)試結(jié)束回到 Serverless Desktop 頁面,我們可以看到輸出效果:

這里我們發(fā)起調(diào)用是向這個(gè)服務(wù)的根目錄發(fā)起,如果我們想向其他的路由地址發(fā)起調(diào)用該怎么操作呢?我們可以復(fù)制發(fā)起調(diào)用后輸出的基礎(chǔ)地址。

然后貼到 postman, 再往后拼接上我們的測試路由地址,比如,想訪問"/appCenter/getSpecial",可以拼接成"<基礎(chǔ)地址>/appCenter/getSpecial"然后"Send"這個(gè)請求。



通過這樣的方式我們可以深入細(xì)節(jié)知道每一行代碼的調(diào)用問題到底出在哪里,極大的提高了我們的開發(fā)效率。
調(diào)試結(jié)束后不要忘了清楚調(diào)試環(huán)境,包括關(guān)閉本地的容器地址,以及清理線上的函數(shù)。

綜上我們完成了一次完整的調(diào)試過程。
數(shù)據(jù)庫使用
Serverless 一大特色就是他的計(jì)費(fèi)模式:按需按量計(jì)費(fèi)。在數(shù)據(jù)庫存儲層面,阿里云推出 Serverless 分布式數(shù)據(jù)庫 Tablestore,和函數(shù)計(jì)算 FC 是最佳拍檔。
1、前提
請先到 tablestore 控制臺,開通服務(wù)
使用主賬號創(chuàng)建按量付費(fèi)的實(shí)例

在實(shí)例管理頁面獲取訪問地址以及實(shí)例名

首先讓我們先認(rèn)識下 initializer 函數(shù),initializer 函數(shù)能夠保證統(tǒng)一實(shí)例成功并且僅成功執(zhí)行一次。這個(gè)特性非常適合我們的數(shù)據(jù)庫初始化連接。
const TableStore = require('tablestore');let internal; // 先定義一個(gè)全局變量exports.initializer = (context, callback) => {try {const endpoint = process.env.tablestore_endpoint; // tablestore的連接地址const instanceName = process.env.tablestore_instanceName;// tablestore實(shí)例名const tableClient = new TableStore.Client({accessKeyId: context.credentials.accessKeyId,accessKeySecret: context.credentials.accessKeySecret,stsToken: context.credentials.securityToken,endpoint,instancename: instanceName,});internal = { tableClient, TableStore };callback();} catch (err) {callback(err.message);}}
數(shù)據(jù)庫建立連接后,我們就可以在 handler 函數(shù)中處理業(yè)務(wù)邏輯。
const { http } = require('@serverless-devs/dk');http// 創(chuàng)建表.post("/table", async (ctx, next) => {const { tableName } = ctx.request.body;const { tableClient } = ctx.req.requestContext.internal;const params = {tableMeta: {tableName,primaryKey: [{name: 'id',type: 'INTEGER',},],},reservedThroughput: {capacityUnit: {read: 0,write: 0,},},tableOptions: {timeToLive: -1, // 數(shù)據(jù)的過期時(shí)間, 單位秒, -1代表永不過期. 假如設(shè)置過期時(shí)間為一年, 即為 365 * 24 * 3600.maxVersions: 1, // 保存的最大版本數(shù), 設(shè)置為1即代表每列上最多保存一個(gè)版本(保存最新的版本).},streamSpecification: {enableStream: true, //開啟StreamexpirationTime: 24, //Stream的過期時(shí)間,單位是小時(shí),最長為168,設(shè)置完以后不能修改},};await tableClient.createTable(params);ctx.body = {success: true,message: `${tableName}表已創(chuàng)建成功`,}})exports.handler = (req, res, context) => {context.internal = internal;http()(req, res, context);};
1)Serverless Devs 提供 tablestore 組件, 通過一行命令快速體驗(yàn)
$ s init dk-tablestore2)本地調(diào)試
$ cd code$ npm install$ npm run serve
3)一鍵部署到函數(shù)計(jì)算 FC,回到項(xiàng)目根目錄(s.yaml 平級),執(zhí)行命令
$ s deploy
項(xiàng)目源碼
如果大家覺得文章對你有幫助,請點(diǎn)點(diǎn)手指給我們的項(xiàng)目加個(gè)星,項(xiàng)目發(fā)展需要您的助力:
https://github.com/Serverless-Devs/Serverless-Devs
Serverless Hub Demo 源碼地址
數(shù)據(jù)庫 tablestore Demo 源碼地址
使用更多框架開發(fā) API Demo 源碼地址
社區(qū)網(wǎng)址一覽
社區(qū)官網(wǎng)
http://www.serverless-devs.com/
項(xiàng)目倉庫
https://github.com/Serverless-Devs/Serverless-Devs
Serverless Desktop 桌面客戶端
https://serverlessdevs.resume.net.cn/zh-cn/desktop/index.html
Serverless 應(yīng)用開發(fā)者套件
http://serverless-dk.oss.devsapp.net/docs/tutorial-dk/intro/react
Serverless Devs CLI
https://serverlessdevs.resume.net.cn/zh-cn/cli/index.html
Serverless Hub 應(yīng)用中心
https://serverlesshub.resume.net.cn/#/hubs/special-view
場景體驗(yàn)地址:
https://developer.aliyun.com/adc/expo/serverless

釘釘識別二維碼,進(jìn)群參與討論
??戳“閱讀原文”, 立即體驗(yàn) Serverless Devs!
