騰訊云云函式 SCF 最近新發布了 Node.js 12.16 的 runtime,也是國內首家支持 Node.js 12.x 的主流云服務商,
Node.js 版本的升級帶來了新的特性以及性能方面的提升,有興趣的同學可以參考國外一博主總結的文章《Node.js 12: The future of server-side JavaScript》了解具體內容,
其中比較重要的一點是啟動速度提升,通過 v8 code cache 的支持,構建時提前為內置庫生成代碼快取,提升 30% 的啟動耗時,
騰訊云云函式 SCF 為了讓 Serverless 更加符合 Node.js 原生的使用體驗,針對 Node.js runtime 做了針對性的優化,
借這個機會,我想和大家分享一下如何使用騰訊云云函式來開發 Node.js 應用以及 scf 的 Node.js runtime 實作的原理,
入口函式的引數
首先我們看一下最基本的 Node.js 入口函式:
exports.main_handler = (event, context, callback) => {
console.log("Hello World");
console.log(event);
console.log(context);
callback(null, event);
};
runtime 會將三個引數傳遞到處理程式方法,
第一個引數是 event,用來傳遞觸發事件資料
包含來自呼叫程式的資訊,呼叫程式在呼叫時將該資訊作為 JSON 格式字串傳遞,事件結構因服務而異,
-
定時觸發器的 event 物件就包括了觸發的時間,觸發器的名稱
{Message: "", Time: "2020-05-08T14:30:00Z", TriggerName: "time_5", Type: "Timer"} -
apigateway 觸發器的 event 物件透傳了 http 請求的完整內容以及 apigateway 定制化的 http 請求頭部資訊
{"headerParameters":{},"headers":{...},"httpMethod":"GET","path":"/params_log","pathParameters":{},"queryString":{},"queryStringParameters":{},"requestContext":{"httpMethod":"ANY","identity":{},"path":"/params_log","serviceId":"service-9khp96qy","sourceIp":"120.229.9.165","stage":"release"}}
- cos 觸發器的 event 物件包括了觸發執行的具體 cos 操作以及 cos 物件
{"Records":[{"cos":{"cosBucket":{"appid":"1251133793","name":"test","region":"gz"},"cosNotificationId":"unkown","cosObject":{"key":"/1251133793/test/xxx.png","meta":{"Content-Type":"image/png","x-cos-request-id":"NWViNTZmMmFfOTJhODQwYV80MGZmXzI0Y2ZkYmM="},"size":6545739,"url":"...","vid":""},"cosSchemaVersion":"1.0"},"event":{"eventName":"cos:ObjectCreated:Put","eventQueue":"qcs:0:scf:ap-guangzhou:appid/1251133793:default.params_log.$DEFAULT","eventSource":"qcs::cos","eventTime":1588948779,"eventVersion":"1.0","reqid":1038862404,"requestParameters":{"requestHeaders":{"Authorization":"..."},"requestSourceIP":"120.229.9.165"},"reservedInfo":""}}]}
第二個引數 context,函式運行時資訊
我們來看一下一個完整的 context 包含的內容:
callbackWaitsForEmptyEventLoop: true,
getRemainingTimeInMillis: 200,
memory_limit_in_mb: 128,
time_limit_in_ms: 3000,
environment: "{"SCF_NAMESPACE":"demo","TENCENTCLOUD_SECRETID":"...","TENCENTCLOUD_SECRETKEY":"...","TENCENTCLOUD_SESSIONTOKEN":"..."}"
function_name: "params",
function_version: "$LATEST",
namespace: "demo",
request_id: "ab42b693-8bfd-4dc1-b228-60360a63e06c",
tencentcloud_appid: "...",
tencentcloud_region: "ap-chengdu",
tencentcloud_uin: "..."
從上面的內容可以看到,該物件包含的內容有:
- 函式配置資訊,比如設定的內容大小,超時時間等
- 執行身份認證資訊,如果設定了函式的運行角色(角色必須要包含對應操作的授權策略),在環境變數中會注入 secretId,secretKey,sessionToken,在訪問第三方云服務,比如 cos、自定義監控資料上報時就可以使用這幾個值直接呼叫云 api,而不用在代碼里面去 hard code 各種密鑰資訊
- 環境變數:包括了用戶自定義的環境變數以及一些系統環境變數
- 執行環境基本資訊:包括了當前函式呼叫的地域,用戶的 appId,uin
第三個引數 callback 是一個可選引數,在非異步函式中回傳執行結果
回呼函式采用兩個引數:一個 Error 和一個回傳,回傳物件必須與 JSON.stringify 兼容,異步函式將忽略 callback 的回傳,必須通過 return、throw exception 或者 promise 來處理回傳或錯誤
const https = require('https')
let url = "https://cloud.tencent.com/"
exports.main_handler = function(event, context, callback) {
https.get(url, (res) => {
callback(null, res.statusCode)
}).on('error', (e) => {
callback(Error(e))
})
}
函式回傳
我們來看一下,針對異步場景(async 函式)和非異步場景,云函式怎么把回傳值傳遞出去
異步函式
對于異步函式,可以使用 return 和 throw 來發送回傳或錯誤,函式必須使用 async 關鍵字,在異步函式中,第三個引數 callback 沒有定義
示例:異步函式
const https = require('https')
let url = "https://cloud.tencent.com/"
const httpRequest = url => {
const promise = new Promise(function(resolve, reject) {
https
.get(url, res => {
resolve(res.statusCode)
})
.on('error', e => {
reject(Error(e))
})
})
return promise
}
exports.handler = async function(event, context) {
try{
const result = await httpRequest(url)
// 在async函式中callback未定義
// callback(null, result)
return result
}catch(e) {
throw e
}
}
同步函式
還是上面的例子,發起一個 http 請求,如果用同步函式實作,參照以下示例
示例:同步函式,callback 回傳
const https = require('https')
let url = "https://cloud.tencent.com/"
exports.handler = function(event, context, callback) {
https.get(url, (res) => {
// 只能通過callback回傳,return會被忽略
callback(null, res.statusCode)
}).on('error', (e) => {
callback(Error(e))
})
}
回傳的時機
正常的 Node.js web framework 在 response 回傳后,異步邏輯還是繼續在執行的,而 Serverless 場景下,由于機制和 framework的差別,對于已經回傳 responese 的情況,一種是等著異步都處理完再來回傳,這樣保證了一次呼叫的完整性,另外一種就是在回傳后就直接結束當次呼叫,直接掛起異步處理,
騰訊云云函式針對 Node.js 的異步場景,實作了回傳和結束分離的特殊機制,

- 入口函式的同步執行程序完成及回傳后,云函式的呼叫將立刻回傳,并將代碼的回傳資訊回傳給函式呼叫方
- 同步流程處理并回傳后,代碼中的異步邏輯可以繼續執行和處理,直到異步事件執行完成后,云函式的實際執行程序才完成和退出,
默認情況下,函式執行會等待所有異步執行結束才算一次呼叫結束,但也給用戶提供了關閉事件回圈等待的選項,用戶可以關閉事件回圈等待來自行控制函式的回傳時機,通過在 callback 回呼執行前設定 context.callbackWaitsForEmptyEventLoop = false,可以使云函式在執行回傳后立刻凍結行程,不再等待異步回圈內的事件
比如一下示例代碼,代碼里面發起了一個異步 http 請求,另外有一個 2s 后執行的 setTimeout
const https = require('https')
let url = "https://cloud.tencent.com/"
const httpRequest = url => {
const promise = new Promise(function(resolve, reject) {
https
.get(url, res => {
resolve(res.statusCode)
})
.on('error', e => {
reject(Error(e))
})
})
return promise
}
exports.main_handler = async function(event, context) {
// 設定該選項為false會不等待異步佇列執行完,而是在回傳后直接凍結process
//context.callbackWaitsForEmptyEventLoop = false
try{
const result = await httpRequest(url)
setTimeout(() => {
console.log('timeout log')
}, 2000)
return result
}catch(e) {
throw e
}
}
在 http 請求完成后,會立即回傳給呼叫方,不會等待 setTimeout 的異步實踐執行完,而在回傳后,程式會繼續執行,直到 setTimeout 的事件執行完才算本次呼叫結束,
在設定了 context.callbackWaitsForEmptyEventLoop = false 后,在 return 后行程會被凍結,setTimeout 里面的執行邏輯會被掛起
完整流程圖
以下是單實體內 runtime 運行的完整流程圖

針對 Node.js應用,有以下幾個實踐建議:
- 日志:runtime重寫了 console 的幾個主要方法,而且是在 require 用戶檔案之后,所以用戶自定義日志選項會無效
- 快取復用:在入口函式外可以定義變數,存盤可以復用的快取物件,比如資料庫的連接等
Node.js 的模塊實作邏輯中,如果一個 module 被 require 過,該模塊就會被 cache 到記憶體中,再次被 require時不會重新初始化,針對這一特性,如果實體一直再復用,那么在入口檔案中,入口函式外定義的變數都不會被銷毀,可以達到復用的效果 - 內置部分 npm 包,可以直接使用,具體參照檔案,部署云函式代碼時推薦
npm install --production,減少代碼包的體積,提升上傳速度和執行速度 - 執行角色:配置執行角色,從 context 中可以獲取臨時的密鑰資訊,可以用了訪問有相應權限的第三方服務,而不用在代碼內寫死密鑰資訊
回放:點擊觀看 Tencent Serverless Hours 線上分享會第一期
Serverless Framework 30 天試用計劃
我們誠邀您來體驗最便捷的 Serverless 開發和部署方式,在試用期內,相關聯的產品及服務均提供免費資源和專業的技術支持,幫助您的業務快速、便捷地實作 Serverless!
詳情可查閱:Serverless Framework 試用計劃
One More Thing
3 秒你能做什么?喝一口水,看一封郵件,還是 —— 部署一個完整的 Serverless 應用?
復制鏈接至 PC 瀏覽器訪問:https://serverless.cloud.tencent.com/deploy/express
3 秒極速部署,立即體驗史上最快的 Serverless HTTP 實戰開發!
傳送門:
- GitHub: github.com/serverless
- 官網:serverless.com
歡迎訪問:Serverless 中文網,您可以在 最佳實踐 里體驗更多關于 Serverless 應用的開發!
推薦閱讀:《Serverless 架構:從原理、設計到專案實戰》
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/14646.html
標籤:其他
