Serverless 多函數開發示例
01.
什麼是 Serverless?
Serverless 的定義和理解在不同的角度和場景會有不同的解讀,AWS 將 Serverless(在 AWS 雲上)定義爲 “是一種用於描述服務、實踐和策略的方式,使您能夠構建更敏捷的應用程序,從而能夠更快地創新和響應變化” 的一種服務。而紅帽認爲 Serverless 是 “可使開發人員專注構建和運行應用,而無需管理服務器” 的一種開發模型,並進一步將 Serverless 的產品分爲兩類:BaaS(後端即服務,讓開發人員訪問各種各樣的第三方服務和應用))與 FaaS(功能即服務,開發人員編寫邏輯,部署到完全由平臺管理的容器中,然後按需執行)兩種形態。而 Serverless Framework 則認爲 Serverless 是 “一場由開發人員和企業推動, 讓單個開發人員可以完成高流量的應用開發,同時只將精力集中在產生價值的方面” 的運動,
不管哪個方面,哪種角度,Serverless 都具有以下共同特點:
-
快速開發,快速部署;
-
按量付費,降低成本;
-
自動擴容,無需維護;
而目前都是基於各個雲廠商的 FaaS 服務來實現,如:騰訊雲的 SCF,AWS 的 Lambda,Azure 雲的 Azure Funcitons 等。
Serverless 解決什麼問題?
隨着計算能力的加強,系統複雜度的增加,用戶規模的增長,軟件問題(如下, 也稱爲軟件危機)也會發生指數型的增長。
-
軟件開發進度難以預測
-
軟件開發成本難以控制
-
軟件產品質量無法保證
-
軟件產品難以維護
而 Serverless 則可以通過以下方式提出了對於軟件危機問題的解決方案:
-
通過函數方式將系統功能拆分爲更小的顆粒度,更便於設計,開發,測試和維護。
-
通過按量計費大幅度減少資源閒置時的開銷費用,降低服務器成本。
-
通過自動擴容以及雲平臺的支持,大幅減少運維工作量以及軟件維護成本。
同時在現在普遍倡導敏捷工作方式的現代工作環境中,Serverless 也爲快速驗證想法、迭代功能提供了開發方式的最佳實踐,同時而不需要擔心代碼改動會影響系統的其他功能,也無需考慮部署前的服務器配置以及部署後的維護工作。
02.
Serverless Framework
Serverless Framework 是業界非常受歡迎的無服務器應用框架,通過與衆多一流雲供應商如騰訊雲,AWS 等的緊密合作,爲廣大開發者提供無需關心底層基礎設施,即可編寫和部署代碼的無服務開發體驗。
Serverless Framework 同時提供資源管理、自動伸縮、統計分析等能力,讓廣大開發者可以節省運維成本,真正做到 “按量付費” 的同時,也無需花費精力處理日誌收集、異常統計等任務。
Serverless Framework 通過 CLI 工具與騰訊雲緊密合作,爲中國用戶提供了基於組件(Serverless Components) 的完整解決方案。覆蓋了無服務應用編碼、測試、部署等全生命週期,同時切合中國用戶的使用場景和習慣。
爲什麼選用 Serverless Framework
通過 Serverless Framework 的短短几行配置文件和 CLI 工具,開發者就可以額外獲得:
-
在本地進行函數開發,並一鍵部署到雲端,無需額外適配雲函數,也無需登錄控制檯。
-
支持將傳統開發框架的應用 (如:Express, Next.js, Flask, Laravel 等)部署爲 Serverless 應用。
-
在本地對函數代碼進行調試,或使用遠程開發模式在本地實時查看部署服務的日誌輸出,並進行調試。
-
通過簡單配置即可完成所有基礎設施配置(如:API 網關、COS 存儲、DB 鏈接等)
-
快速切換應用的部署環境(開發,演示,生產)地區。
-
更詳細輕鬆的瞭解應用狀態,查看日誌、報錯統計等信息。
03.
多函數開發示例
本示例使用 Serverless Framework 的多函數組件(multi-scf)和 PostgreSQL 組件(postgresql),實現以下 3 個 API 接口。
-
GET /todos/
獲取所有的 todo 事項 -
POST /todos/
創建新的 todo 事項 -
POST /todos/{id}/actions/complete
完成 todo 事項
並使用 Serverless Framework 提供的 invoke 和 logs 功能進行調試以及查看生產環境實時日誌。
本示例相關代碼可以在 Git 倉庫中獲取。
- Git 倉庫地址:https://github.com/ole3021/sls-demo-msn-todo
步驟 1:安裝 Serverless Framework
執行以下命令安裝 Serverless Framework
$ npm install serverless -g
如果之前您已經安裝過 Serverless Framework,可以通過下列命令升級到最新版:
$ npm update serverless -g
此命令會安裝最新的 Serverless Framework 到你的計算機,安裝成功後可以通過 serverless
或者 sls
開始使用 Serverless Framework
步驟 2:初始化多函數項目
$ npm update serverless -g
此命令會使用應用模板 multi-scf-nodejs
初始化名爲 my-multi-scf-demo
的應用目錄。初始化成功後該目錄結構爲:
.
├── README.md
├── index.js
└── serverless.yml
這裏的文件用途如下:
-
index.js:函數文件。
-
serverless.yml:Serverless Framework 配置文件。
-
app:應用名稱,會作爲應用識別的唯一標識。
-
stage:應用環境,通過不同環境,部署不同的應用實例。
-
component:組件名稱
-
name:組件實例名稱
-
inputs:組件部署的輸入參數
步驟 3:鏈接數據庫
因爲 Serverless 是無狀態的(運行後就會銷燬), 所以這裏需要鏈接數據庫用來持久化 todo 信息。添加數據庫需要先借助 VPC 網絡連接。
1. 添加 VPC
創建子目錄 vpc
並在子目錄中添加新的 serverless.yml
文件如下:
component: vpc # [必選]要使用組件,更多組件請查看 https://github.com/serverless-components
name: sls-demo-msn-vpc # [必選]組件實例名稱
inputs:
region: ap-guangzhou # 實例所屬地區
zone: ap-guangzhou-2 # 實例所屬地區區域
vpcName: ${name} # 實例名稱,這裏複用字段 name 作爲名稱。
subnetName: sls-demo-msn-subnet # 子網的名稱
更多 VPC 的配置內容,查看 VPC 私有網絡 獲取更多詳情信息。
- VPC 私有網絡:serverless.com/cn/framework/docs/infrastructure/vpc/
在子組件的配置文件中,app 名稱會自動繼承父目錄的 serverless.yml 中的配置。同時同一個應用的 app 名稱需要保持一致。
2. 添加數據庫
創建子目錄 db
並在子目錄中添加新的 serverless.yml
文件如下:
component: postgresql #(必填) 引用 component 的名稱,當前用到的是 postgresql 組件
name: sls-demo-msn-DB # (必填) 該 postgresql 組件創建的實例名稱
inputs:
region: ap-guangzhou # 實例所屬地區
zone: ap-guangzhou-2 # 實例所屬地區區域
dBInstanceName: ${name}-${stage} # 數據庫實例名稱唯一,且同一個數據庫只能存在同一個vpc內。
extranetAccess: true # 是否開啓實例外網訪問
vpcConfig: # vpc網絡配置
vpcId: ${output:${stage}:${app}:sls-demo-msn-vpc.vpcId} # 私有網絡Id
subnetId: ${output:${stage}:${app}:sls-demo-msn-vpc.subnetId} # 子網Id
在數據庫配置中添加數據庫到 vpc 網絡,這裏使用輸出變量 (output) 來動態獲取 vpc 的 id 信息。
更多變量的配置內容,查看 Serverless 變量 獲取更多詳情信息。
- Serverless 變量:serverless.com/cn/framework/docs/basic/variables/
更多 PostgreSQL 的配置內容,查看 PostgreSQL 數據庫 獲取更多詳情信息。
- PostgreSQL:serverless.com/cn/framework/docs/infrastructure/postgresql/
在組件部署完成後,可以在組件目錄內,使用
sls info
查看組件的輸出變量,或者可以到騰訊雲的應用控制檯查看相關信息。
3. 初始化應用目錄
-
創建子目錄
src
並將創建生成的index.js
(重命名爲todos.js
) 和serverless.yml
移動到目錄中。 -
在
src
目錄中執行npm init
初始化 Node.js 項目。 -
在
src
目錄中執行npm i pg --save
安裝數據庫鏈接依賴包pg
。 -
在項目根目錄添加根配置文件
serverless.yml
,文件如下:
app: sls-demo-msn-todo-3e5a2134 # 應用唯一識別標識,同賬號下需要保持唯一。
stage: dev # 應用部署環境名稱,這裏使用環境變量 STAGE 的值。
根目錄的配置文件信息會被子組件繼承,不需要在子組件中重複定義。(僅限於 app 與 stage)。
最終完成的項目目錄結構如下:
.
├── README.md
├── db # 數據庫
│ └── serverless.yml # 數據庫配置文件
├── serverless.yml
├── src # 多函數應用
│ ├── node_modules
│ ├── package-lock.json
│ ├── package.json # Node.js依賴文件
│ ├── serverless.yml # 多函數應用配置文件
│ └── todos.js # todo 應用主文件
└── vpc # vpc
└── serverless.yml # vpc配置文件
4. 修改多函數應用配置
在多函數目錄src
內修改配置文件如下:
component: multi-scf
name: sls-demo-msn
inputs:
src:
src: ./
exclude:
- .env
- "node_modules/**" # 部署時忽略node_modules目錄中所有文件,以加快部署速度
environments: # 應用環境變量信息
- key: PG_CONNECT_STRING
value: ${output:${stage}:${app}:sls-demo-msn-DB.private.connectionString}
region: ap-guangzhou
runtime: Nodejs12.16
memorySize: 128
vpc: # vpc網絡配置
vpcId: ${output:${stage}:${app}:sls-demo-msn-vpc.vpcId} # 私有網絡Id
subnetId: ${output:${stage}:${app}:sls-demo-msn-vpc.subnetId} # 子網Id
installDependency: true # 是否在線安裝依賴
timeout: 6 # 默認超時時間(秒)
functions: # 多函數定義
allTodo: # 函數別名
handler: todos.all # 處理函數
memorySize: 256 # 自定義次函數的內存空間
addTodo:
handler: todos.add
timeout: 9 # 自定義此函數的超時時間(秒)
completeTodo:
handler: todos.comp
timeout: 9
triggers: # 觸發器配置
- type: apigw
parameters:
name: todosAPIGW
protocols:
- https
- http
apis: # API配置
- path: /todos/ # 路由路徑
method: GET # 路由方法
function: allTodo # 路由處理函數別名
- path: /todos/
method: POST
function: addTodo
- path: /todos/{id}/actions/complete
method: POST
function: completeTodo
param: # 動態路由參數配置
- name: id
position: PATH
required: true
type: number
desc: Todo ID
這裏修改主要內容有:
-
使用
installDependency
開啓部署後依賴自動安裝並忽略node_module
目錄下的全部文件 (無需上傳 node_modules,加快部署) -
使用
vpc
添加 vpc 網絡並鏈接到項目同一個 vpc 網絡中。 -
使用
environments
添加項目環境變量,並使用輸出變量(output)來動態生成數據庫連接字符串。 -
使用
functions
來聲明項目中的函數及其別名。 -
使用
triggers
聲明函數的觸發器,並在觸發器的apis
中配置各個函數對應的路徑,以及參數信息。
更多函數開發的配置內容和詳情,查看 PostgreSQL 數據庫獲取更多詳情信息:
- PostgreSQL 數據庫:https://www.serverless.com/cn/framework/docs/infrastructure/postgresql/。
更多 函數開發 的說明內容,查看 文檔鏈接 獲取更多詳情信息:
- 文檔鏈接:https://www.serverless.com/cn/framework/docs/function/。
步驟 4:開發功能
修改 todos.js
完成相關功能的開發,最終該文件代碼如下:
"use strict";
const { Client } = require("pg");
const client = new Client({
connectionString: process.env.PG_CONNECT_STRING,
});
/**
* 初始化數據庫和表結構
*/
const initDB = async () => {
const isConnected = client && client._connected;
if (!isConnected) {
await client.connect();
await client.query(`
CREATE TABLE IF NOT EXISTS todo (
ID SERIAL NOT NULL,
TITLE VARCHAR NOT NULL,
NOTE TEXT,
IS_COMPLETE BOOLEAN DEFAULT FALSE
);`);
}
};
/**
* 獲取所有Todo事項
*/
exports.all = async (event, context) => {
// async 需要關閉事件循環等待,以避免日誌記錄超時或函數無返回的問題。
context.callbackWaitsForEmptyEventLoop = false;
await initDB();
const { rows } = await client.query({ text: "SELECT * FROM todo" });
return {
message: "Tencent SCF execute successful!",
data: rows,
};
};
/**
* 添加新的Todo事項
*/
exports.add = async (event, context) => {
// async 需要關閉事件循環等待,以避免日誌記錄超時或函數無返回的問題。
context.callbackWaitsForEmptyEventLoop = false;
const { title, note } = JSON.parse(event.body);
if (!title) {
return {
statusCode: 400,
message: "Missing Todo Title",
};
}
await initDB();
const { rowCount } = await client.query({
text: "INSERT INTO todo (title, note) VALUES($1, $2)",
values: [title, note],
});
return rowCount === 1
? {
statusCode: 201,
message: "Todo added success.",
}
: {
statusCode: 400,
message: "Todo added failed.",
};
};
/**
* 完成指定Todo事項
*/
exports.comp = async (event, context) => {
// async 需要關閉事件循環等待,以避免日誌記錄超時或函數無返回的問題。
context.callbackWaitsForEmptyEventLoop = false;
const todoId = event.pathParameters.id;
if (!todoId && !isNaN(todoId)) {
return {
statusCode: 400,
message: "Missing Todo Id",
};
}
await initDB();
const { rowCount } = await client.query({
text: "UPDATE todo SET is_complete = true WHERE id=$1",
values: [todoId],
});
return rowCount === 1
? {
statusCode: 200,
message: "Todo Complete success.",
}
: {
statusCode: 400,
message: "Todo Complete failed.",
};
};
步驟 5:調試功能
1. Invoke 調試
要調試代碼除了使用第三方開發工具通過配置的 API 網關 url 調試,還可以使用 Serverless Framework 的 Invoke 功能 或 遠程調試 功能. 這裏使用 invoke 功能演示如何調試函數功能。
invoke 和 遠程調試功能 需要在組件的目錄內執行。
2. 獲取全部 Todo
在 src 目錄下執行;
$ serverless invoke -f allTodo
執行後可以得到結果
使用授權信息 default 授權中,如果需要使用臨時密鑰,請使用 --login 重新登陸
billDuration: 36
duration: 36
errMsg:
functionRequestId: fe6d302d-f6db-42ad-9c7b-8d0c61ead9b3
invokeResult: 0
log:
"""
START RequestId: fe6d302d-f6db-42ad-9c7b-8d0c61ead9b3
Event RequestId: fe6d302d-f6db-42ad-9c7b-8d0c61ead9b3
END RequestId: fe6d302d-f6db-42ad-9c7b-8d0c61ead9b3
Report RequestId: fe6d302d-f6db-42ad-9c7b-8d0c61ead9b3 Duration:36ms Memory:256MB MemUsage:11.3984MB
"""
memUsage: 11952128
---------------------------------------------
Serverless: 調用成功
{
message: 'Tencent SCF execute successful!',
data: []
}
在 invoke 返回的結果中,會包含函數執行後的 meta 信息,如運行時間,錯誤,RequestId,執行的日誌 和函數返回的結果。
3. 創建新的 Todo
在 src 目錄下執行
$ serverless invoke -f addTodo --data "{\"body\":\"{\\\"title\\\":\\\"Create multi-scf project demo\\\",\\\"note\\\":\\\"Todo App with postgreSQL\\\"}\"}"
執行後可以得到的結果;
使用授權信息 default 授權中,如果需要使用臨時密鑰,請使用 --login 重新登陸
billDuration: 35
duration: 35
errMsg:
functionRequestId: 93f50016-064f-468d-9e98-31645fc254fd
invokeResult: 0
log:
"""
START RequestId: 93f50016-064f-468d-9e98-31645fc254fd
Event RequestId: 93f50016-064f-468d-9e98-31645fc254fd
END RequestId: 93f50016-064f-468d-9e98-31645fc254fd
Report RequestId: 93f50016-064f-468d-9e98-31645fc254fd Duration:35ms Memory:128MB MemUsage:11.293MB
"""
memUsage: 11841536
---------------------------------------------
Serverless: 調用成功
{
statusCode: 201,
message: 'Todo added success.'
}
步驟 6:部署和日誌
1. 部署代碼到生產環境
使用下面命令可以快速部署項目到生產環境(這裏命名生產環境爲prod
);
$ serverless deploy --stage prod
2. 即時查看生產環境日誌
在項目目錄src
中執行以下命令可以查看項目的即時日誌信息;
$ sls logs --tail -f allTodo --stage prod
以下是返回結果:
使用授權信息 default 授權中,如果需要使用臨時密鑰,請使用 --login 重新登陸
serverless ⚡components
Action: "logs" - Stage: "prod" - App: "sls-demo-msn-todo-3e5a2134" - Name: "sls-demo-msn"
START RequestId:6f31857109130f092c547337c073ea91
Response RequestId:dbb3a8ed63a32be8e6b7a2dd8a32bbe2 RetMsg:{"message":"Tencent SCF execute successful!","data":[{"id":1,"title":"Create multi-scf project demo","note":"Todo App with postgreSQL","is_complete":false}]}
END RequestId:dbb3a8ed63a32be8e6b7a2dd8a32bbe2
Report RequestId:dbb3a8ed63a32be8e6b7a2dd8a32bbe2 Duration:4ms Memory:256MB MemUsage:12.113281MB
Response RequestId:485a87cfc6ad385b7e9c84343962391b RetMsg:{"message":"Tencent SCF execute successful!","data":[{"id":1,"title":"Create multi-scf project demo","note":"Todo App with postgreSQL","is_complete":false}]}
END RequestId:485a87cfc6ad385b7e9c84343962391b
Report RequestId:485a87cfc6ad385b7e9c84343962391b Duration:4ms Memory:256MB MemUsage:11.886719MB
START RequestId:0ede6d26dca55362a701c10ff51c9021
Serverless › 監聽中 ...
總結
感謝長久以來對 Serverless Framework 支持的廣大開發者,未來我們也會繼續迭代產品,推出新功能,改進產品使用體驗,最終我們會爲中國的開發者打造一套符合中國開發者習慣的無服務器開發的完整解決方案。
也歡迎大家到 Serverless 中文社區分享經驗和想法以及反饋問題和 Bug。
- Serverless 中文社區:https://github.com/serverless/serverless-tencent/discussions
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/PbxPamEeqWTH09WfUYOQ1w