了解uniCloud之云函数和云对象-1

云函数是运行在云端的js代码,是基于nodejs扩展的。每一个云函数就是一个js包,里面还可以有package.json作为配置文件。
云函数启动后实例会保留一段时间(如15分钟),超过保留期后若该云函数一直没有被再调用,那这个实例会被释放。所以云函数有冷启动的概念。不过由于js实例的启动要比php和java更快,所以js更适合serverless方式。

云函数和客户端通信

传统restfulcallfunction云对象clientDB
ajaxuniapp客户端通过uniCloud.callFunction(functionname) 调用云函数uniapp客户端通过uniCloud.importObject(functionname)调用云对象uniapp客户端通过 调用uniCloud.database()API访问云端数据。 还可以通过action云函数追加服务器逻辑。
适用域名注册的情况下,uniapp不推荐使用这个方式。相比传统方式,call function更安全,不会暴露域名和ip ,不怕攻击,也不需要注册域名。跟callfunction比,代码更简洁,开发更高效,uniapp3.4起支持。推荐使用。

clientDB适用的情况:
客户端操作云数据库(无论增删改查),那么推荐使用clientDB方式,由uni-app客户端直接操作云数据库。

操作数据库的同时,还需要同时执行一些云端逻辑,推荐使用数据库触发器替代action云函数。

clientDB不适用的情况:
1)请求不操作数据库。
2)操作数据库请求不希望暴露到前端。
3)数据库表多字段多接口少。
4)权限体系复杂的。

——云对象
云对象和clientDB的区别,云对象可以适用于clientDB不适用的地方。

// 客户端发起调用云函数hellocf,并传入data数据
uniCloud.callFunction({
	name: 'hellocf',
	data: {a:1,b:2}
}).then((res) => {
	console.log(res.result) // 结果是 {sum: 3}
}).catch((err) => {
	console.error(err)
})

// 云函数hellocf的代码,接收到客户端传递的data,并对其中a和b相加返回给客户端
'use strict';
exports.main = async (event, context) => {
	//event为客户端上传的参数
	console.log('event : ', event)
	//此处省略event.a和event.b的有效性校验
	//返回数据给客户端
	return {sum : event.a + event.b}
};



uniCloud API(官方文档

——访问其他HTTP
uniCloud.httpclient.request(URL,requestOptions)
requertOptions的值看文档
直接上代码

const res = await uniCloud.httpclient.request('api',{
 method: 'POST',
    data: {
      test: 'testValue'
    },
    contentType: 'json', // 指定以application/json发送data内的数据
    dataType: 'json' // 指定返回值为json格式,自动进行parse
  })
console.log(res)

//formData类型数据
const fs = require('fs')
const path = require('path')
const FormData = require('form-data'); 
exports.main = async (event, context) => {
  const form = new FormData()
  form.append('media', fs.readFileSync(path.resolve(__dirname, './test.jpg')),{      
    filename: 'test.jpg',
    contentType: 'image/jpeg'
  });
  form.append('otherParam', 'otherParam content');
  const res = await uniCloud.httpclient.request('https://api/post', {
    method: 'POST',
    content: form.getBuffer(), // 请求内容
    headers: form.getHeaders(), // 请求头
    dataType: 'json' // 此处指定为json表示将此请求的返回值解析为json
  })
  return res
};

请求和环境API,实例uniCloud对象,与reuqest请求是一对多关系。
获取请求id列表(uniCloud.getRequestList()),如果配置了单实例多并发,那么,uniCloud对象无法确定是当前请求中数组中哪一个,所以,有方法来确定当前请求是哪一个:uniCloud.getUniCloudRequertId(),或云函数自带参数context。

扩展库:短信uni-cloud-sms,一键登录uni-cloud-verify,推送uni-cloud-push…

——公共模块
目前云函数上传包大小上限10m,如果超过,只能弃阿里云选择腾讯云。

云函数/云对象中调用云函数
调用uniCloud.callfunction,但不支持callback形式。
给10万用户发信息,一个短信接口支持给50个用户发信息,那么需要2000次请求,云函数完不成,要用云函数递归做,上例子:

// 当前云函数名称 send-sms-cf
'use strict';
const db = uniCloud.database();
const dbCmd = db.command
const userTable = db.collection('uni-id-users')
exports.main = async (event, context) => {
	//执行业务逻辑
	let res = await sendSms(event.before_id)
	if (res.errCode) {
		return res
	}else{
		// 如果没有报错,就让当前云函数 调用当前云函数(云对象同理)。注意:这里是异步的
		uniCloud.callFunction({
			name: 'send-sms-cf',
			data: {
				before_id: res.before_id
			}
		}).catch(e=>{
			console.log(e.message);
		}).then(e=>{
			console.log(e.result);
		})
		
		// 等待500毫秒,给一个请求发出去的时间
		return await new Promise((resolve, reject) => {
			setTimeout(() => {
				resolve(res)
			}, 500)
		})
	}

	async function sendSms(before_id) {
		console.log('before_id',before_id);
		let where = {
			phone: dbCmd.exists(true),
			//..这里可以写你自己的其他条件,如超过多久没登录的用户 last_login_date < Date.now() - 3600*24*...
		}
		if(before_id){
			//高性能分页查询,以上一次查询的最后一条数据的id被起始id
			where._id = dbCmd.gt(before_id)
		}
		
		let res = await userTable.where(where)
			.limit(50)
			.orderBy("_id", "asc")
			.get()

		if (!res.data.length) {
			return {
				errCode: 'sendSms-invalid',
				errMsg: '结束,没有符合条件的接收者'
			}
		}
		let phoneList = res.data.map(item => item.phone)
		let sendSmsRes = await uniCloud.sendSms({
			phoneList,
			appid: '__UNI__xxxxxxx',
			smsKey: '****************',
			smsSecret: '****************',
			templateId: '100**', // 请替换为自己申请的模板id
			data: {
				text1: 'xxx',
				text2: 'xxx'
			}
		})
		if (sendSmsRes.errCode) {
			return sendSmsRes
		}
		return {
			errCode: 0,
			before_id: res.data[res.data.length - 1]._id
		}
	}
};

云函数内部访问其他服务空间
腾讯云服务空间的云函数内支持获取同账号下其他服务空间的uniCloud实例。
注意:本地调试云函数,存在跨服务空间问题,那么使用云端云函数。

serverless引发的一些概念:冷启动、实例、并发请求、无状态、伪全局变量。

——冷启动:serverless实例化——加载云函数——启动node——执行云函数;实例会保留一段时间,在这期间客户端再次请求云函数,不会启动冷启动,速度会快。
——热启动:云函数实例和进程复用,性能更好,它只需要做一件事,执行云函数。
(优化冷启动:阿里云单实例多并发,腾讯云付费实例子预留,非高频云函数合并到高频云函数中或设置定时任务持续运行它。)

实例和请求:实例相当于云函数的运行环境,或者说node进程…实例和请求不是一对一的,所以获取客户端信息时,不是在实例的全局对象上获取,而是在请求的上下文上获取。

——云函数无状态和全局变量
实例可能是第一次启动,也可能是启动了,云函数js中的全局变量就是伪变量,是云函数无状态的。
在云对象中,module.exports ={},云函数中exports.main = async (event,context)=>{},它们之前的全局变量,是伪全局变量。
它们在实例被保留的期间内多次请求中复用,因此,会出现变量累加的情况。
上代码:

let count = 0;
exports.main = async (event, context) => {
	return count++
}

根据上面代码,count首次为0,如果发生复用,那么count会是1,2,3…(当然可以用这种方法获得重复次数),require由于存在缓存,也存在同样问题,所以尽量不要修改require返回的结果。
uniCloud全局对象也是跨请求,请求相关的内容不应该挂载到uniCloud的全局对象上。
正确的全局变量,应该怎样:
uni-config-center:静态全局变量可以使用uni提供的配置中心。
redis:动态全局变量使用redis。

——请求的上下文
先了解实例和请求关系:
通过uniCloud.getRequestList(),可以获得实例请求id列表;
通过uniCloud.getClientInfos(),可以获得所有请求客户端信息;
uniCloud私有云,想要获取与请求相关的信息,无法从uniCloud全局变量上获得;云函数的event,context和请求相关,云对象每次请求都对应一个单独this,用来解决问题。

——单实例多并发,看例子

// 开启单实例多并发前的uni-id用法
const uniID = require('uni-id')
exports.main = async function(event, context) {
  const res = uniID.login({
    // ...一些参数
  })
  return res
}

// 由于uni-id默认会从一个内置全局变量上获取客户端平台信息,不同请求会修改此全局变量可能造成混乱,开启单实例多并发后需要将uni-id修改为如下写法
let uniID = require('uni-id')
exports.main = async function(event, context) {
  let uniIDIns = uniID.createInstance({ 
  // 创建uni-id实例,其上方法同uniID
    context: context // 传入context防止不同请求互相影响
    // config: {} // 完整uni-id配置信息,使用config.json进行配置时无需传此参数
  })
  const res = uniIDIns.login({
    // ...一些参数
  })
  return res
}

——云函数中的异步行为

——return策略:阿里云终止后,逻辑就不会在执行;腾讯云node12之后可以配置是否执行,开启了,记得关闭,云函数未终止会一直收费,还会中断redis和服务器建立的链接。(package.json 的keepRunningAfterReturn设置为true,中断reids:redis.quit(),下次使用时重新建立🔗uniCloud.redis())
——云函数配置:运行内存,固定出口ip,超时时间

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

罗罗666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值