腾讯云云函数 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 应用的开发!