我的知识记录分享
云函数在云端Node.js的运行机制与本地Node.js会有一些差异,主要表现在云函数实例是由事件触发不是始终运行的(执行完随时会销毁),各个实例之间在横向上是相互隔离的(没有公用的内存或硬盘空间),在纵向上是无状态的(云函数的执行不能调用上一次云函数的执行信息)。云函数平台通过弹性伸缩实例来支持高并发,实例也存在冷启动、热启动(实例复用)的情况。除了机制不同,云函数是无服务器Serverless架构,配置上与传统的Node.js服务器也有所不同。
在配置云函数的环境变量之前,我们需要先来了解云函数的process.env属性,它会返回包含用户环境的对象。process对象是Node.js的全局对象,无需使用require()就可以使用。
比如在invoke云函数里输入以下代码,上传部署到云端之后,在开发者工具的console调用,然后查看云函数的日志就可以看到env环境变量对象:
const cloud = require('wx-server-sdk')
cloud.init({ env:cloud.DYNAMIC_CURRENT_ENV
})
exports.main = async(event, context) => { console.log("env环境变量",process.env)
}
env环境变量里包含一些内置的环境变量key,比如以SCF_
、QCLOUD_
、 TECENTCLOUD_
开头的key是无法配置的。env对象的一些属性,我们可以在云函数中直接获取到它的具体值并在代码中会用。
比如SCF_RUNTIME函数运行时的Node.js版本,SCF_FUNCTIONVERSION是云函数的版本,TENCENTCLOUD_APPID是云开发对应的腾讯云账号APPID,使用方法如下:
const cloud = require('wx-server-sdk')
cloud.init({ env:cloud.DYNAMIC_CURRENT_ENV
})
exports.main = async(event, context) => { const {SCF_RUNTIME,SCF_FUNCTIONVERSION,TENCENTCLOUD_APPID} = process.env return {SCF_RUNTIME,SCF_FUNCTIONVERSION,TENCENTCLOUD_APPID}
}
打开云开发控制台云函数标签页,我们可以选择一个云函数比如invoke,然后点击版本管理,云函数更新迭代比较频繁且版本变更比较频繁时,就需要对新版本的云函数发布一个灰度版本以来了解新版云函数的执行情况,这时候就可以使用云函数的灰度/版本管理(这里就不深入介绍了)。 而我们要配置云函数的环境变量,可以点击配置,进入云函数的配置页,在环境变量处,我们可以给云函数以key和value的方式配置环境变量。
在配图中,我们写了一些环境变量的大致示例,这些变量的值可以通过process.env.key
来获取,环境变量常用于如下应用场景:
TZ
为Asia/Shanghai
即可。
在云函数的调用日志里,我们可以看到云函数被调用的执行时间和执行内存。默认情况下,云函数的配置内存为256MB,而一般情况下云函数的执行内存都会在100MB以内。
如果根据项目需要,你希望使用云函数来执行图片、音视频、爬虫等任务的处理,云函数的执行内存可能会超过256M,这个时候你可以将云函数的内存升级到512M、1024M等;而如果你只是使用云函数处理一些简单的任务,你可以将云函数的内存将配到128M。
在前面我们已经说过,云函数有个计费指标资源使用量GBs,它的值为云函数的配置内存 X 运行计费时长,也就是说根据业务情况合理配置云函数的内存,可以降低云函数的成本。
云函数默认的超时时间是3秒钟,通常情况下,这个时长已经足够了;一般来说,如果日志里云函数的执行时长超过300毫秒,就应该检查云函数是否需要优化了。不仅如此,对于一些密集型的任务,也建议使用定时触发器来解决,比如当需要使用云函数发送几十万条短信时,可以借助于定时触发器分批发送,如每5秒,而不是使用云函数一次性发送完(关于定时触发器,教程后面有介绍)。
不过如果根据业务需求,云函数会下载一些文件,或处理的链路比较长,或处理的量相对比较大(比如几千条短信之类的),3s的执行时间可能会不够,这时候就需要将云函数的超时时间的值设置更大一些。云函数的超时时间最大可以设置为60s,不过建议一般不要超过20s。
使用开发者工具在invoke云函数目录下新建一个common文件用于存放一些模块文件,然后再在common文件夹下新建common.js文件:
invoke // 云函数目录 ├── common //common文件夹 │ └── common.js //common.js文件 └── index.js
└── config.json
└── package.json
在common.js输入以下代码,在common文件里就包含一些通用的数据对象、函数等:
const key = { AppID: 'wxda99ae45313257046', AppKey: 'josgjwoijgowjgjsogjo',
} const getName = (msg) => { return msg+'李东bbsky';
}; //判断是否为数字 const validateNumber = n => !isNaN(parseFloat(n)) && isFinite(n) && Number(n) == n; //元素在数组的index位置 const indexOfAll = (arr, val) => arr.reduce((acc, el, i) => (el === val ? [...acc, i] : acc), []);
exports.key = key
exports.getName = getName
exports.validateNumber = validateNumber
exports.indexOfAll = indexOfAll
在index.js里输入以下代码,注意模块文件的引入以及模块里的数据对象、函数等接口的调用方法:
const cloud = require('wx-server-sdk')
cloud.init({ env:cloud.DYNAMIC_CURRENT_ENV
}) const common = require('./common/common.js'); const {key,getName,validateNumber,indexOfAll} = common
exports.main = async(event, context) => { const msg = "你好啊" console.log(getName(msg)) console.log(key.AppID) console.log(validateNumber(msg)) console.log(indexOfAll([1, 2, 3, 1, 2, 3], 1))
}
建议在云函数的exports.main函数之外只定义常量或者公共方法,不要定义变量。main函数之外声明的变量可能会被缓存,而不是每次都执行:
const cloud = require('wx-server-sdk')
cloud.init({ env:cloud.DYNAMIC_CURRENT_ENV
}) let i = 0; //比如这里在main函数之外定义了一个变量i exports.main = async(event, context) => {
i++; console.log(i); return i;
};
在第一次调用该云函数的时候函数返回的结果为1,这是符合预期的。但如果连续调用这个云函数,返回值有可能是2或从2开始递增,也有可能又变成1,这便是实例复用的结果。
当云函数热启动时,执行函数的Node.js进程会被复用,进程的上下文也得到了保留,所以变量i自增。当云函数冷启动时,Node.js进程是全新的, 代码会从头完整的执行一遍,此时就会返回1。所以,开发者在编写云函数时,应注意保证云函数是无状态的、幂等的,即当次云函数的执行不依赖上一次云函数执行过程中在运行环境中残留的信息。
使用云函数也可以使用cloud.callFunction
接口来调用其他云函数(可以是同一云开发环境的云函数,也可以是同一账号下或跨账号其他云开发环境里的云函数)。比如云支付的支付成功回调函数就是用云函数调用云函数。对于日常的业务,通常不太建议这种调用链路过长的方式,会比较影响性能。
我们还可以使用switch...case
将多个云函数集成到一个云函数里,其中switch语句会评估一个表达式,将表达式的值与case子句匹配,并执行与该情况相关联的语句:
const cloud = require('wx-server-sdk')
cloud.init({ env:cloud.DYNAMIC_CURRENT_ENV
})
exports.main = async(event, context) => { console.log(event.action) switch (event.action) { //根据调用云函数时传入的action的值来调用不同的函数 case 'addPost': { return addPost(event)
} case 'deletePost': { return deletePost(event)
} case 'updatePost': { return updatePost(event)
} case 'getPost': { return getPost(event)
} default: { return }
}
}; async function addPost(event) { return '创建一篇文章' //这里只是返回一个字符串,可以换成其他的函数,比如在数据库里创建一篇文章 } async function deletePost(event) { return '删除一篇文章' } async function updatePost(event) { return '更新一篇文章' } async function getPost(event) { return '获取一篇文章' }
调用云函数时,
wx.cloud.callFunction({ name:"post", data:{ action:"addPost" // }
}).then(res=>{ console.log(res)
})
将多个云函数集成到一个云函数里除了可以通过
switch...case
的方法外,还可以使用tcb-router和severless-http,这些在教程的第三部分、第四部分都有详细的讲解。
发表评论: