騰訊云云函數 SCF 最近新發布了 Node.js 12.16 的 runtime,也是國內首家支持 Node.js 12.x 的主流雲服務商。javascript
Node.js 版本的升級帶來了新的特性以及性能方面的提高,有興趣的同窗能夠參考國外一博主總結的文章《Node.js 12: The future of server-side JavaScript》瞭解具體內容。html
其中比較重要的一點是啓動速度提高,經過 v8 code cache
的支持,構建時提早爲內置庫生成代碼緩存,提高 30% 的啓動耗時。java
騰訊云云函數 SCF 爲了讓 Serverless 更加符合 Node.js 原生的使用體驗,針對 Node.js runtime 作了針對性的優化。
借這個機會,我想和你們分享一下如何使用騰訊云云函數來開發 Node.js 應用以及 scf 的 Node.js runtime 實現的原理。node
首先咱們看一下最基本的 Node.js 入口函數:git
exports.main_handler = (event, context, callback) => { console.log("Hello World"); console.log(event); console.log(context); callback(null, event); };
runtime 會將三個參數傳遞處處理程序方法。github
包含來自調用程序的信息。調用程序在調用時將該信息做爲 JSON 格式字符串傳遞,事件結構因服務而異。web
定時觸發器的 event 對象就包括了觸發的時間,觸發器的名稱
{Message: "", Time: "2020-05-08T14:30:00Z", TriggerName: "time_5", Type: "Timer"}
數據庫
apigateway 觸發器的 event 對象透傳了 http 請求的完整內容以及 apigateway 定製化的 http 請求頭部信息express
{"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"}}
npm
{"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 包含的內容:
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: "..."
從上面的內容能夠看到,該對象包含的內容有:
回調函數採用兩個參數:一個 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應用,有如下幾個實踐建議:
npm install --production
,減小代碼包的體積,提高上傳速度和執行速度咱們誠邀您來體驗最便捷的 Serverless 開發和部署方式。在試用期內,相關聯的產品及服務均提供免費資源和專業的技術支持,幫助您的業務快速、便捷地實現 Serverless!
3 秒你能作什麼?喝一口水,看一封郵件,仍是 —— 部署一個完整的 Serverless 應用?
複製連接至 PC 瀏覽器訪問:https://serverless.cloud.tencent.com/deploy/express
3 秒極速部署,當即體驗史上最快的 Serverless HTTP 實戰開發!
傳送門:
- GitHub: github.com/serverless
- 官網:serverless.com
歡迎訪問:Serverless 中文網,您能夠在 最佳實踐 裏體驗更多關於 Serverless 應用的開發!