cf-functions.md 60.7 KB
Newer Older
Q
qiang 已提交
1 2 3

## 简介@intro

4
云函数是运行在云端的 `JavaScript` 代码,是基于 `Node.js` 的扩展。
Q
qiang 已提交
5

6
在常规的 `Node API` 基础上,uniCloud的云函数环境内置了`uniCloud`对象,这个对象内置了网络、数据库等各种API。开发者未学习过 `Node.js` 也没有关系,只需要看uniCloud的文档,掌握这个`uniCloud`对象的API即可。
Q
qiang 已提交
7

W
wanganxp 已提交
8
每个云函数是一个js包,在云函数被调用时,由 serverless 调度系统分配硬件资源启动一个 node 环境来运行这个云函数。
9

W
wanganxp 已提交
10
在HBuilderX中可以新建云函数(HBuilderX 3.4 同时可以新建云对象)。
11 12
![](https://bjetxgzv.cdn.bspapp.com/VKCEYUGU-dc-site/a18b3bb0-53d8-11eb-8ff1-d5dcf8779628.jpg)

W
wanganxp 已提交
13
每个云函数是一个目录,其中普通云函数有`index.js`入口文件,云对象的入口文件则是`index.obj.js`
14

W
wanganxp 已提交
15
一个最简单的云函数只需要这个入口js文件,在里面编写代码即可。当然也可以在这个js中require该云函数目录下的其他js、json文件。
16

W
wanganxp 已提交
17
云函数的配置文件和 npm规范 相同,在云函数目录下可新建一个 package.json 来存放配置。uniCloud云函数扩展了 package.json,增加了一些特有的配置项。[详见](/uniCloud/cf-functions?id=packagejson)
雪洛's avatar
雪洛 已提交
18

19
云函数启动后环境会保留一段时间(如15分钟),超过保留期后若该云函数一直没有被再调用,那这个环境会被释放。所以云函数有冷启动的概念。不过由于js环境的启动要比php和java更快,所以js适合serverless方式。
20 21 22

**注意事项**
- 云函数内使用commonjs规范,不可使用import、export,参考:[commonjs模块](http://nodejs.cn/api/modules.html#modules_modules_commonjs_modules)
W
wanganxp 已提交
23 24 25 26
- 不同项目使用同一个服务空间时,不可使用同名云函数。同名云函数会相互覆盖。
- 在HBuilderX创建云函数时,如果新云函数与服务器上已存在同名云函数,会用新函数覆盖。所以应先选择从服务空间下载云函数。
- 单个云函数大小限制为10M(包含node_modules),过大的云函数影响运行性能,也会增加计费的gbs。
- uniCloud的阿里云版,暂不可使用相对路径读取文件(比如`fs.readFileSync('./info.txt')`),可以使用绝对路径`fs.readFileSync(path.resolve(__dirname,'./info.txt'))`
27 28 29

## 云函数的分类

30 31
云函数有若干子概念,包括 普通云函数、云对象、公共模块、clientDB的action云函数、uniCloud扩展库。

W
wanganxp 已提交
32 33 34 35 36
- 云函数:通过传统json接口方式和客户端通信,客户端使用`uniCloud.callfunction("")`调用云函数
- 云对象:是通过前端导入对象来操作的,客户端使用`uniCloud.importObject("")`导入云对象。详见[云对象](/uniCloud/cloud-obj)
- 公共模块:用于不同的云函数/云对象,抽取和共享相同代码,详见[公共模块文档](/uniCloud/cf-functions?id=公共模块)
- action云函数:为了弥补clientDB客户端直接操作数据库的局限而设计的,详见[clientDB action文档](/uniCloud/clientdb?id=action)
- uniCloud扩展库:为了裁剪和控制云函数体积而设计的,一些不太常用的功能比如Redis,独立为扩展库,避免增大每个云函数的体积,详见[uniCloud扩展库](/uniCloud/cf-functions?id=扩展库)
37

W
wanganxp 已提交
38
HBuilderX中uniCloud项目的云函数均在项目的`uniCloud/cloudfunctions`目录下,目录结构如下:
39 40 41 42 43 44 45 46 47 48

<pre v-pre="" data-lang="">
	<code class="lang-" style="padding:0">
|——— cloudfunctions               云函数目录
|   │───common                    云函数公用模块目录 <a target="_blank" href="https://uniapp.dcloud.net.cn/uniCloud/cf-common">详情</a>
|   |   └──hello-common           云函数公用模块
|   |      │──index.js            公用模块代码
|   |      └──package.json        公用模块package.json
|   │───uni-clientDB-actions
|   │      └──new_action.js       clientDB action代码 <a target="_blank" href="https://uniapp.dcloud.net.cn/uniCloud/clientdb?id=action">详情</a>
雪洛's avatar
雪洛 已提交
49 50 51
|   │───function-name             云函数目录
|   │     │──index.js             云函数代码
|   │     └──package.json         包含云函数的配置信息,如url化、定时设置、可用内存等内容 <a target="_blank" href="https://uniapp.dcloud.net.cn/uniCloud/cf-functions?id=packagejson">详情</a>
52 53 54
|   └───object-name               云对象目录
|         │──index.obj.js         云对象代码
|         └──package.json         包含云对象的配置信息,可用内存等内容 <a target="_blank" href="https://uniapp.dcloud.net.cn/uniCloud/cf-functions?id=packagejson">详情</a>
55 56 57
	</code>
</pre>

58 59
## 客户端和云函数的通信@clientcallfunction

W
wanganxp 已提交
60 61
uni-app客户端和传统服务器通信时,使用`uni.request`的ajax请求方式。uniCloud下不再使用它,有更好的云端一体的通信方式。

62 63 64 65 66
uniCloud体系里,客户端和服务端的云函数通信,有4种方式:

|			|传统的restful方式|callfunction方式|云对象方式|clientDB方式|
|:-:|:-:|:-:|:-:|:-:|
|简述		|通过配置[云函数URL化](/uniCloud/http),把云函数转为传统的http链接	|云函数默认并不自带http链接|把callfunction的函数式调用,升级为模块化的对象调用|客户端直接操作云数据库|
J
Jenux 已提交
67
|前端调用方式|传统ajax|uni-app客户端通过`uniCloud.callFunction(functionname)`来调用云函数|uni-app客户端通过`uniCloud.importObject(objectname)`导入一个云对象,直接使用这个对象的方法	|uni-app客户端通过`<uniCloud-db>`组件或`uniCloud.database()` API来访问uniCloud数据库。也支持搭配action云函数追加服务器逻辑	|
W
wanganxp 已提交
68
|适用场景	|http链接需要自己注册域名。如果前端是uni-app,则不推荐使用URL化。如果是非uni-app的系统需要访问云函数,只能使用URL化	|相比云函数URL,callfunction更加安全、更serverless,不暴露域名和ip,不怕攻击,也无需注册域名|uni-app 3.4起支持。相比callfunction方式。代码更加精简、逻辑更加清晰、开发更加高效	|如果uni-app前端发起的服务器请求目的主要是查询或操作数据库,则推荐使用clientDB方式|
69

W
wanganxp 已提交
70 71 72
云函数是uniCloud的基础,本质上 clientDB 和 云对象 都是建立在云函数上针对特定场景的优化。
- clientDB针对的场景是数据库操作,它优化了可以不写或少写服务器代码
- 云对象针对的场景是非数据库操作或不宜前端暴露的数据库操作时,和uni-app客户端的通信方式。它优化了代码结构,更精简、简单
73 74 75 76


### clientDB方式

W
wanganxp 已提交
77
- clientDB适用的情况:
78

W
wanganxp 已提交
79
如果客户端使用uni-app开发,且向uniCloud服务空间的请求主要是为了操作云数据库(无论增删改查),那么推荐使用clientDB方式,由uni-app客户端直接操作云数据库。
80

W
wanganxp 已提交
81
如果操作数据库的同时,还需要同时执行一些云函数,可以使用clientDB的action云函数。
82

W
wanganxp 已提交
83
- clientDB不适用的情况:
84

85 86 87 88
1. 请求不操作云数据库,比如向外部web系统发请求、操作redis、删除云文件等;
2. 操作的云数据库请求不希望暴露在前端;
3. 数据库表和字段数量多而接口数量少。给每个数据配置权限的工作量超过了控制少数接口权限的工作量;
4. 权限体系较复杂,除了用户和管理员外还有较多其他权限条件或动态权限。此时在schema和action中编写代码的复杂度超过了写接口。
89

W
wanganxp 已提交
90
**直观体验代码示例**
91

W
wanganxp 已提交
92
clientDB分API方式和组件方式,此处使用API方式来演示
93
```js
W
wanganxp 已提交
94 95 96 97 98 99 100
// 客户端js直接操作云数据库,查询list表的数据。无需服务器代码
const db = uniCloud.database() // 获取云数据库的引用
db.collection('list').get()
  .then((res)=>{
    // res 为数据库查询结果
  }).catch((err)=>{
    console.log(err); 
101 102 103
  })
```

104
由于篇幅较长,学习clientDB需另见文档[clientDB](clientdb.md)
Q
qiang 已提交
105

W
wanganxp 已提交
106
### 云对象方式
107

108
云对象和clientDB最大的区别,是云对象把数据库操作(以及其他逻辑)封装在云对象的方法里面。
Q
qiang 已提交
109

110
它无法像clientDB那样无需开发服务器代码,它仍需在客户端和云端分别写代码。但它的应用场景不受限制。上文中不适用clientDB的情况,都可以使用云对象解决。
雪洛's avatar
雪洛 已提交
111

W
wanganxp 已提交
112
**直观体验代码示例**
雪洛's avatar
雪洛 已提交
113

W
wanganxp 已提交
114
云端云对象代码,云对象名称:testco,有一个sum方法
Q
qiang 已提交
115 116

```js
W
wanganxp 已提交
117 118 119 120 121
module.exports = {
	sum(a, b) {
		// 此处省略a和b的有效性校验
		return a + b
	}
Q
qiang 已提交
122 123 124
}
```

W
wanganxp 已提交
125
然后在客户端的js中,import这个testco对象,调用它的sum方法
Q
qiang 已提交
126 127

```js
W
wanganxp 已提交
128 129 130 131 132 133 134 135
const testco = uniCloud.importObject('testco') //第一步导入云对象
async function sum () { //注意方法或生命周期需使用async异步方式
	try {
		const res = await testco.sum(1,2) //导入云对象后就可以直接调用该对象的方法了,注意使用异步await
		console.log(res) // 结果是3
	} catch (e) {
		console.log(e)
	}
Q
qiang 已提交
136 137 138
}
```

139
由于篇幅较长,学习云对象需另见文档[云对象](cloud-obj.md)
Q
qiang 已提交
140

141 142 143 144
clientDB和云对象可以混合使用:
1. 比如官方提供了[uni-id-pages](uni-id-pages.md),是基于云对象的登录注册系统,开发者可以导出这个插件处理账户体系,然后剩余的业务如果不算复杂,就可以使用clientDB搞定。
2. 一个业务的用户端和admin端也可以是不同的技术栈。比如业务端有复杂的动态权限,而管理端只有一个admin管理员使用,那么admin端使用[schema2code](schema2code.md)会非常高效,而这些技术都基于clientDB。

W
wanganxp 已提交
145
### 普通云函数callFunction方式
Q
qiang 已提交
146

W
wanganxp 已提交
147
- 普通云函数适用的情况:
Q
qiang 已提交
148

149 150 151 152 153
在HBuilderX 3.5.2之前,需要URL化和定时运行时,只能使用普通云函数;在HBuilderX 3.5.2+,云对象也支持了URL化和定时运行。

官方不推荐开发者使用云函数,有相关需求推荐使用云对象替代云函数。

目前官方还未提供基于云对象的router模式的框架,有相关需求可以使用三方框架。
Q
qiang 已提交
154

W
wanganxp 已提交
155
**直观体验代码示例**
雪洛's avatar
雪洛 已提交
156

W
wanganxp 已提交
157 158 159 160 161 162 163 164 165 166 167
```js
// 客户端发起调用云函数hellocf,并传入data数据
uniCloud.callFunction({
	name: 'hellocf',
	data: {a:1,b:2}
}).then((res) => {
	console.log(res.result) // 结果是 {sum: 3}
}).catch((err) => {
	console.error(err)
})
```
Q
qiang 已提交
168 169

```js
W
wanganxp 已提交
170
// 云函数hellocf的代码,接收到客户端传递的data,并对其中a和b相加返回给客户端
Q
qiang 已提交
171 172
'use strict';
exports.main = async (event, context) => {
W
wanganxp 已提交
173 174 175 176 177
	//event为客户端上传的参数
	console.log('event : ', event)
	//此处省略event.a和event.b的有效性校验
	//返回数据给客户端
	return {sum : event.a + event.b}
178
};
Q
qiang 已提交
179

W
wanganxp 已提交
180
```
181

W
wanganxp 已提交
182
由于篇幅较长,需另见文档[云函数callfunction方式](/uniCloud/cf-callfunction)
W
wanganxp 已提交
183
<!-- 
W
wanganxp 已提交
184 185 186 187 188 189 190 191 192 193 194 195 196
因文档地址迁移,为防止老链接失效,备份如下:
#### 获取用户token@client-token
文档已迁移至:[普通云函数callFunction](/uniCloud/cf-callfunction.md?id=client-token)
#### 获取客户端IP@clientip
文档已迁移至:[普通云函数callFunction](/uniCloud/cf-callfunction.md?id=clientip)
#### 获取客户端user-agent@client-user-agent
文档已迁移至:[普通云函数callFunction](/uniCloud/cf-callfunction.md?id=client-user-agent)
#### 获取服务空间信息@context-space-info
文档已迁移至:[普通云函数callFunction](/uniCloud/cf-callfunction.md?id=context-space-info)
#### 获取云函数调用来源@context-source
文档已迁移至:[普通云函数callFunction](/uniCloud/cf-callfunction.md?id=context-source)
#### 其他客户端信息@client-info
文档已迁移至:[普通云函数callFunction](/uniCloud/cf-callfunction.md?id=client-info)
W
wanganxp 已提交
197
 -->
W
wanganxp 已提交
198
### 云函数URL化方式
199

200 201 202
可以让云函数/云对象生成一个HTTP URL。这样非uni-app应用,可以通过ajax请求和云函数/云对象通信。

在 uniCloud Web控制台进行URL化配置。
203

204
由于篇幅较长,需另见文档[云函数URL化](http.md)
205 206


W
wanganxp 已提交
207
### uniCloud响应体规范@resformat
208

W
wanganxp 已提交
209
`uniCloud响应体规范`(uniCloud response format),是DCloud制定的、服务器给客户端返回json数据的一种建议格式。
210

W
wanganxp 已提交
211
云对象、clientDB、uni-id公共模块均支持此规范。
212 213 214

**由来**

W
wanganxp 已提交
215
uniCloud服务器给客户端返回的数据格式一般是json,但json的格式具体是什么没有约定。比如返回错误码,是叫`code`还是叫`errCode`?错误内容是`message`还是`errMsg`?内容的国际化如何处理?
216 217 218

如果没有一套统一的格式,在客户端将无法编写有效的网络拦截器,无法统一处理错误。

W
wanganxp 已提交
219
另外,如果不同的插件,云端返回的数据格式千差万别,那使用者整合这些插件也会非常麻烦。国际化更无法落地。
220 221 222

为此DCloud推出了`uniCloud响应体规范`

W
wanganxp 已提交
223 224 225
为了与uni-app前端的API错误回调风格统一,uniCloud响应体规范定义的云端返回信息(尤其是报错时)应包含`errCode``errMsg`

除此之外响应体规范还包含`newToken`字段,用于token的自动续期(云对象接收含有newToken的响应后会自动更新storage内存储的uni_id_token及uni_id_token_expired,此行为新增于`HBuilderX 3.4.13`)。开发者一般无需关心此数据,uni-app客户端和云端uni-id之间会自动管理token及续期。
雪洛's avatar
雪洛 已提交
226 227

示例如下:
228

W
wanganxp 已提交
229
```json
230 231
// 失败返回值
{
W
wanganxp 已提交
232 233
  "errCode": 'uni-id-account-banned',
  "errMsg": '账号被禁用'
234
}
W
wanganxp 已提交
235
```
236

W
wanganxp 已提交
237
```json
238 239
// 成功返回值
{
W
wanganxp 已提交
240 241 242 243 244 245
  "errCode": 0,
  "errMsg": '登录成功',
  "uid": 'xxx', // 其他信息
  "newToken": { // 用于下发新token给客户端
	  "token": 'xxx',
	  "tokenExpired": 'xxx'
雪洛's avatar
雪洛 已提交
246
  }
247 248 249
}
```

250 251 252 253 254 255 256
HBuilderX内使用代码块`returnu`可以快速输入以下代码(`HBuilderX 3.4.0`及以上版本): 

```js
return {
	errCode: 0,
	errMsg: ''
}
257 258
```

259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
- errCode

errCode在成功时应返回数字`0`,失败时应返回一个以插件id开头的“字符串”,每个单词以连字符(`-`)分割。做出这样的规定是为了防止不同插件之间出现重复错误码

`'uni-id-account-banned'`错误码为例,`uni-id`为插件id,`account-banned`为错误缩写。

如果业务开发的代码并不发布插件市场,那么为了避免下载了一个市场的插件产生冲突,推荐使用不包含“-”的字符串来做errCode(插件市场的所有插件ID必须包含“-”)。

后续uniCloud会提供自动根据errCode对errMsg进行国际化处理的功能,开发者仅需保证云函数返回值满足`uniCloud响应体规范`即可。

- errMsg

errMsg用于存放具体错误信息,包括展示给开发者、终端用户的错误信息

## uniCloud API列表

275 276 277
云函数支持 js 和 nodejs 的标准API,如`console.log()``setTimeout()`,另见[nodejs官网](https://nodejs.org/en/docs/)。nodejs版本,详见[云函数运行环境](?id=runtime)

除了标准API外,云函数环境中内置了`uniCloud`对象,扩展了一批新API,实际开发中更常用的是uniCloud的扩展API。见下:
278

雪洛's avatar
雪洛 已提交
279 280 281
|API						|描述																																			|
|--							|--																																				|
|uniCloud.database()		|云数据库对象 [详情](uniCloud/cf-database.md)																									|
282 283
|uniCloud.databaseJQL()		|云函数中使用JQL语法操作数据库 [详见](uniCloud/jql-cloud.md),需添加扩展库																			|
|uniCloud.redis()			|使用redis [详见](uniCloud/redis.md),需添加扩展库																							
雪洛's avatar
雪洛 已提交
284 285 286 287 288
|uniCloud.uploadFile()		|云函数上传文件到云存储 [详情](uniCloud/storage?id=clouduploadfile)																				|
|uniCloud.downloadFile()	|云函数下载云存储的文件到云函数运行环境 [详情](uniCloud/storage?id=clouddownloadfile)															|
|uniCloud.deleteFile()		|云函数删除云存储的文件 [详情](uniCloud/storage?id=clouddeletefile)																				|
|uniCloud.getTempFileURL()	|获取云存储文件的临时路径 [详情](uniCloud/storage?id=cloudgettempfileurl)																		|
|uniCloud.customAuth()		|使用云厂商自定义登录,仅腾讯云支持[详情](uniCloud/authentication.md?id=cloud-custom-auth)														|
289
|uniCloud.callFunction()	|云函数/云对象中调用另一个云函数 [见下](?id=callbyfunction)	|
290
|uniCloud.importObject()	|云函数/云对象中调用另一个云对象 [详情](cloud-obj.md?id=call-by-cloud)	|
291
|uniCloud.httpclient		|云函数中通过http访问其他系统 [见下](uniCloud/cf-functions?id=httpclient)																		|
292 293
|uniCloud.sendSms()			|发送短信,需添加扩展库 [详见](uniCloud/send-sms.md)																											|
|uniCloud.getPhoneNumber()	|获取一键登录手机号,需添加扩展库 [详见](uniCloud/univerify.md?id=cloud)																						|
D
DCloud_LXH 已提交
294
|uniCloud.init()			|获取指定服务空间的uniCloud实例 [详见](uniCloud/concepts/space.md?id=multi-space)														|
295
|uniCloud.logger			|云函数中打印日志到[uniCloud web控制台](https://unicloud.dcloud.net.cn/)的日志系统(非HBuilderX控制台)[详情](rundebug.md?id=uniCloudlogger)															|
296

雪洛's avatar
雪洛 已提交
297 298 299 300
## 错误对象@uni-cloud-error

云函数调用uniCloud接口时(包括请求云函数、云对象、云存储等)可能存在抛出错误的场景,此时会抛出uniCloud标准的错误对象(以下记为uniCloudError),uniCloudError包含以下属性

雪洛's avatar
雪洛 已提交
301 302 303 304 305 306
|属性		|类型	|必备	|说明												|
|--			|--		|--		|--													|
|errCode	|string	|是		|错误码												|
|errMsg		|string	|是		|错误信息											|
|requestId	|string	|否		|请求Id,用于排查错误								|
|detail		|object	|否		|仅云对象主动返回错误对应的响应体规范时会有此属性	|
雪洛's avatar
雪洛 已提交
307 308

另外uniCloudError对象上还有code属性和message属性,两者均不推荐使用。
309

Q
qiang 已提交
310 311
## 访问数据库

312 313 314 315
云函数中支持访问本服务空间下的、或经授权的其他服务空间下的,数据库。

- 使用 MongoDB 语法操作数据库,另见[文档](uniCloud/cf-database.md)
- 使用 JQL 语法操作数据库,另见[文档](uniCloud/jql-cloud.md)
Q
qiang 已提交
316

317
## 访问其他HTTP服务@httpclient
Q
qiang 已提交
318

319
云函数中如需要请求其他http服务,则使用`uniCloud.httpclient`。无需额外依赖,就可以请求任何 HTTP 和 HTTPS 协议的 Web 服务。`uniCloud.httpclient`返回的是一个[urllib实例](https://github.com/node-modules/urllib)
Q
qiang 已提交
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339

**uniCloud.httpclient.request(URL,requestOptions)**

**requestOptions参数说明**

|参数名							|类型																																																				|是否必填	|默认值	|说明																																																																																			|
|----								|----																																																				|----			|----		|----																																																																																			|
|method							|String																																																			| -				|GET		|HTTP 请求方法, 默认为:GET. 可选值: GET, POST, DELETE, PUT																																																							|
|data								|Object																																																			| -				|-			|发送的数据																																																																																|
|dataAsQueryString	|Boolean																																																		| -				|true		|是否强制转换data为queryString																																																																						|
|content						|String &#124; Buffer																																												| -				|-			|手动设置请求的payload,设置后会忽略data																																																																	|
|stream							|ReadStream																																																	|-				|-			|发送请求正文的可读数据流																																																																									|
|writeStream				|WriteStream																																																|-				|-			|接受响应数据的可写数据流																																																																									|
|consumeWriteStream	|Boolean																																																		|-				|true		|是否等待 writeStream 完全写完才算响应全部接收完毕																																																												|
|files							|Array&lt;ReadStream&#124;Buffer&#124;String&gt; &#124; Object &#124; ReadStream &#124; Buffer &#124; String| -				|-			|上传的文件,设置后将会使用 multipart/form-data 格式。如果未设置method,将会自动将method设置为POST																																				|
|contentType				|String																																																			| -				|-			|上传数据的格式,设为`json`会自动在`header`内设置`Content-Type: application/json`																																													|
|nestedQuerystring	|Boolean																																																		| -				|-			|转换data为queryString时默认不支持嵌套Object,此选项设置为true则支持转换嵌套Object																																												|
|dataType						|String																																																			| -				|-			|返回的数据格式,可选值为 'json'(返回数据转为JSON),'text'(返回数据转为字符串), ''(返回数据不做处理,默认值)																												|
|fixJSONCtlChars		|Boolean																																																		|-				|false	|在JSON.parse之前处理响应结果中的控制字符(Control Character)																																																						|
|headers						|Object																																																			| -				|-			|请求头																																																																																		|
雪洛's avatar
雪洛 已提交
340
|timeout						|Number &#124; Array																																												| -				|5000			|超时时间设置。设置为数组时第一项为请求超时,第二项为返回超时。设置为数字时相当于同时设置请求超时和返回超时,即`timeout:3000`效果等于`timeouut:[3000,3000]`								|
Q
qiang 已提交
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
|auth								|String																																																			|-				|-			|简单登录授权(Basic Authentication)参数,必须按照 `user:password` 格式设置																																															|
|digestAuth					|String																																																			|-				|-			|摘要登录授权(Digest Authentication)参数,必须按照 `user:password` 格式设置																																															|
|agent							|[http.Agent](https://nodejs.org/api/http.html#http_class_http_agent)																				|-				|-			|http代理,如不使用可设为false																																																																						|
|httpsAgent					|[https.Agent](https://nodejs.org/api/https.html#https_class_https_agent)																		|-				|-			|https代理,如不使用可设为false																																																																						|
|ca									|String&#124;Buffer&#124;Array																																							|-				|-			|证书内容																																																																																	|
|rejectUnauthorized	|Boolean																																																		|-				|true		|是否在证书不受信任时返回错误																																																																							|
|pfx								|String&#124;Buffer																																													|-				|-			|包含了私钥, 证书和CA certs, 一般是 PFX 或者 PKCS12 格式																																																									|
|key								|String&#124;Buffer																																													|-				|-			|PEF格式的服务器的私钥																																																																										|
|cert								|String&#124;Buffer																																													|-				|-			|PEM格式的服务器证书密钥																																																																									|
|passphrase					|String																																																			|-				|-			|私钥或pfx密码的字符串																																																																										|
|ciphers						|String																																																			|-				|-			|使用或排除的cipher																																																																												|
|secureProtocol			|String																																																			|-				|-			|SSL 使用的方法,例如,`SSLv3_method` 强制 SSL 版本为3。																																																									|
|followRedirect			|Boolean																																																		|-				|false	|收到3xx响应时是否自动重定向																																																																							|
|maxRedirects				|Number																																																			|-				|10			|最高重定向次数																																																																														|
|formatRedirectUrl	|Function																																																		|-				|-			|手动格式化url																																																																														|
|beforeRequest			|Function																																																		|-				|-			|请求发送前的钩子																																																																													|
|streaming					|Boolean																																																		|-				|false	|是否直接返回响应流,开启 streaming 之后,HttpClient 会在拿到响应对象 res 之后马上返回, 此时 result.headers 和 result.status 已经可以读取到,只是没有读取 data 数据而已。|
|gzip								|Boolean																																																		|-				|false	|是否支持 gzip 响应格式。开启 gzip 之后,HttpClient 将自动设置 Accept-Encoding: gzip 请求头, 并且会自动解压带 Content-Encoding: gzip 响应头的数据。											|
|timing							|Boolean																																																		|-				|false	|是否开启请求各阶段的时间测量																																																																							|
|enableProxy				|Boolean																																																		|-				|false	|是否启用代理																																																																															|
|proxy							|String																																																			|-				|null		| 代理地址																																																																																|
|lookup							|Function																																																		|-				|-			|自定义DNS查询函数																																																																												|
|checkAddress				|Function																																																		|-				|-			|校验请求地址																																																																															|
|trace							|Boolean																																																		|-				|false	|是否启用捕获堆栈																																																																													|

雪洛's avatar
雪洛 已提交
366 367 368
**注意**

默认情况下request接口不会处理返回的数据,即不传`dataType`参数时会返回buffer类型的数据,如需自动解析json格式的返回结果,需要将`dataType`设置为`"json"`
Q
qiang 已提交
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402

**示例代码**

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

返回数据结构如下

```js
{
	"data": {"name": "DCloud"}, // 响应内容
	"status": 200, // 状态码
	"headers": { // 响应头,仅作示例,不同服务器返回的有差异
		"date": "Tue, 29 Dec 2020 08:10:30 GMT",
		"content-type": "application/json",
		"content-length": "276",
		"connection": "keep-alive",
		"server": "gunicorn/19.9.0",
		"access-control-allow-origin": "*",
		"access-control-allow-credentials": "true"
	}
}

```

W
wanganxp 已提交
403
### 发送formdata类型数据
Q
qiang 已提交
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429

实际业务中常有使用云函数发送formdata类型数据的需求,比如微信小程序提供的一些服务端接口(图片内容安全检测、识别图片二维码等),可以参考以下示例进行发送

```js
'use strict';
const fs = require('fs')
const path = require('path')
const FormData = require('form-data'); // 此form-data需要使用npm安装,地址:https://www.npmjs.com/package/form-data
exports.main = async (event, context) => {
  const form = new FormData()
  form.append('media', fs.readFileSync(path.resolve(__dirname, './test.jpg')), { // 为方便演示此处直接使用云函数目录下的test.jpg文件
    filename: 'test.jpg',
    contentType: 'image/jpeg'
  });
  form.append('otherParam', 'otherParam content');
  const res = await uniCloud.httpclient.request('https://httpbin.org/post', {
    method: 'POST',
    content: form.getBuffer(), // 请求内容
    headers: form.getHeaders(), // 请求头
    dataType: 'json' // 此处指定为json表示将此请求的返回值解析为json
  })
  return res
};

```

雪洛's avatar
雪洛 已提交
430
## 扩展库@extension
雪洛's avatar
雪洛 已提交
431

L
lei 已提交
432
uniCloud的api中,有些api对应的实现,其代码体积较大,且这些功能并不是每一个云函数都会使用。为了方便开发者控制云函数的体积,设计了`uniCloud扩展库`的概念。
433

W
wanganxp 已提交
434 435 436
开发者可以对云函数目录点右键,管理公共模块和扩展库依赖,在其中选择要加载的扩展库。这个可视化界面对应的数据在云函数目录下的 package.json 内的`extensions`字段下。

注意:未引用扩展库的,使用uniCloud相应api时会报错。
437

雪洛's avatar
雪洛 已提交
438

W
wanganxp 已提交
439
**目前支持的扩展库如下**
雪洛's avatar
雪洛 已提交
440

441
- JQL扩展库[uni-cloud-jql]:用于在云函数内使用JQL语法操作数据库,详见:[JQL扩展库](uniCloud/jql-cloud.md)
442 443 444
- redis扩展库[uni-cloud-redis]:云函数内使用redis,详见:[redis扩展库](uniCloud/redis.md)
- 发送短信扩展[uni-cloud-sms]:云函数中发送短信,详见:[sms扩展](uniCloud/send-sms?id=extension)
- 一键登录API扩展[uni-cloud-verify]:手机App调用运营商一键登陆服务时,云函数中获取到真实手机号, 详见:[一键登陆扩展库](uniCloud/univerify?id=extension)
445
- 统一推送服务扩展库[uni-cloud-push]:云函数内使用uni-push,详见:[uniCloud/uni-cloud-push/api]
雪洛's avatar
雪洛 已提交
446

W
wanganxp 已提交
447
以下是一个开启了redis扩展库的云函数package.json示例,注意此文件不支持注释,下方示例中的注释仅为演示
雪洛's avatar
雪洛 已提交
448 449 450 451 452 453 454 455 456 457 458 459 460

```js
{
  "name": "add-article",
  "version": "1.0.0",
  "description": "新增文章",
  "main": "index.js",
	"extensions": {
		"uni-cloud-redis": {} // 配置为空对象即可,后续如有扩展参数会在此处配置
	}
}
```

W
wanganxp 已提交
461 462 463 464
## 公共模块@common

云函数支持公共模块。多个云函数的共享部分,可以抽离为公共模块,然后被多个云函数引用。由于篇幅较长,[详见](uniCloud/cf-common)

465
## 使用npm
Q
qiang 已提交
466

467
云函数的运行环境是 `Node.js`,因此我们可以使用 `npm` 安装第三方依赖。
Q
qiang 已提交
468

469
注意:阿里云目前仅支持全量上传云函数(整个 node_modules文件夹全部上传),因此提醒开发者精简依赖,否则可能会每次上传时间很慢,影响开发体验。并且太大的npm库影响云函数的运行性能。
Q
qiang 已提交
470

471
腾讯云会在上传云函数后自动安装需要的npm依赖。
Q
qiang 已提交
472

473 474
Tips:
- 目前每个云函数上传包大小限制为10M。如果npm包很大,阿里云的整体上传机制会无法满足需求。此时只能选择腾讯云,交给腾讯云自动安装依赖。
Q
qiang 已提交
475 476


477
## 云函数/云对象中调用云函数@callbyfunction
Q
qiang 已提交
478

479
### 调用三方云函数或云对象
雪洛's avatar
雪洛 已提交
480

481
用法同客户端调用云函数,仍然是`uniCloud.callfunction`,但不支持callback形式。
Q
qiang 已提交
482

483
**请求参数**
Q
qiang 已提交
484 485 486 487 488 489

|字段			|类型			|必填	|说明					|
|---			|---			|---	|---					|
|name			|String		|是		|云函数名称。	|
|data			|Object		|否		|云函数参数。	|

490
**响应参数**
Q
qiang 已提交
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507

|字段			|类型		|必备	|说明												|
|---			|---		|---	|---												|
|errCode	|String	|否		|状态码,操作成功则不返回。	|
|errMsg		|String	|否		|错误描述。									|
|result		|Object	|否		|云函数执行结果。						|
|requestId|String	|否		|请求序列号,用于错误排查。	|

**示例代码**

```javascript
let callFunctionResult = await uniCloud.callFunction({
    name: "test",
    data: { a: 1 }
})
```

508 509 510 511 512 513
**注意**

由于调用方不是uni-app客户端,云函数的context、云对象的this.getClientInfo等API无法获取客户端信息,包括 uni-id-token。

可以在云函数互调时手动传递 token ,或者校验调用来源(source)为云函数(function)时不验证用户 token。

514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604
云函数/云对象互相调用时调用方会通过公网访问被调用方,访问速度不如直接将逻辑放在调用方执行。使用前请确保你确实需要此功能。

`HBuilderX 3.4.0`版本之前**云函数右键本地运行时**,里面的代码再次callFunction会调用云端的云函数而不是本地云函数,此bug后续版本已修复。

### 云函数递归调用自身@recurrence

除了调用三方云函数,事实上云函数还可以递归调用自己。

当一个云函数实例的资源不能满足需求,或超时时间不够用时。比如要给10万个用户发送短信,而短信发送接口一次调用最多支持50个手机号码,这样最少需要调用2000次接口才能完成;而一个云函数实例完成不了2000次接口的调用。这种场景就可以使用云函数递归调用,分解任务实现。

示例代码如下:

```js
// 当前云函数名称 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)
		res = await uniCloud.sendSms({
			phoneList,
			appid: '__UNI__xxxxxxx',
			smsKey: '****************',
			smsSecret: '****************',
			templateId: '100**', // 请替换为自己申请的模板id
			data: {
				text1: 'xxx',
				text2: 'xxx'
			}
		})
		if (res.errCode) {
			return res
		}
		return {
			errCode: 0,
			before_id: res.data[res.data.length - 1]._id
		}
	}
};
```

注意:如果不小心把递归云函数写成死循环,就把云函数的内容全部删除,重新上传覆盖即可

雪洛's avatar
雪洛 已提交
605

606
### 云函数内访问其他服务空间@call-by-function-cross-space
雪洛's avatar
雪洛 已提交
607 608 609

> 仅腾讯云支持

610
在腾讯云服务空间的云函数内支持获取**同账号**下其他服务空间的uniCloud实例,参考:[一个应用访问多个服务空间](uniCloud/concepts/space.md?id=multi-space),并使用此实例调用对应服务空间的云函数。
雪洛's avatar
雪洛 已提交
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626

```javascript
//开发者创建了多个服务空间,则需手动初始化。注意这是前端代码,不是云函数代码
const myCloud = uniCloud.init({
  provider: 'tencent',
  spaceId: 'xxxx-yyy'
});
//通过uniCloud实例调用云开发的API
myCloud.callFunction()
myCloud.uploadFile()
```

**注意**

- 连接本地云函数调试时,如果存在跨服务空间调用,则callFunction会使用云端云函数

W
wanganxp 已提交
627 628
## 云函数运行环境说明@runtime

629
云函数运行在 node 环境中。可以使用 node api `process.version` 获取 node 版本。
W
wanganxp 已提交
630

631 632 633
- uniCloud 阿里云默认是 node8.17.0,也可以在 package.json 中选择 node12
- uniCloud 腾讯云默认是 node8.9.4,也可以在 package.json 中选择 node12
- HBuilderX 本地运行环境使用的是 HBuilderX 自带的 node 版本,目前为 node12。在 package.json 选择 node版本 只云端生效,且只在第一次上传云函数时生效。
W
wanganxp 已提交
634 635 636

**注意**
- 本地开发一旦使用了 node12 的专用 api,上传云函数时必须在package.json里手动配置选择 node12 的运行环境。
637
	之所以没有在云端默认统一使用 node12,是因为腾讯云 node12 的 return 策略有一些特殊情况,[见下](?id=return)
W
wanganxp 已提交
638 639 640 641 642
- 运行环境在云端云函数创建时设定,不可通过更新云函数来修改。
	也就是第一次上传云函数的时候,package.json里配了什么,就是什么。如果需要修改,需先删除云端云函数,重新上传。

node版本可以在云函数的package.json文件的`cloudfunction-config->runtime`字段进行配置,详情参考:[云函数package.json](uniCloud/cf-functions.md?id=packagejson)

643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694
### 云函数冷启动、热启动@launchtype

基于云函数按需执行的特点, 函数在不被触发的时候, 计算资源是不被激活的。

当一个云函数初次被触发时,其完整过程如下:

1. severless实例化计算实例
2. 加载函数代码
3. 启动 node
4. 执行代码

函数被调用时,执行这些完整步骤的过程一般称作`冷启动`, 冷启动的耗时长于热启动,一般在一秒出头。 

而如果函数实例和执行进程都被复用的情况下一般被定义为`热启动`, 热启动没有性能问题。

如果一个云函数实例长时间没有被再次调用,则该计算实例会被**回收**;后续再次调用该云函数时,就会再次触发云函数的**冷启动**

不同云厂商的函数实例回收时间不同:
- 阿里云:15分钟内没有第二次访问的云函数,就会被回收
- 腾讯云:30分钟

直观的体验表现为:隔了很久不用的云函数,第一次用就会比较慢,然后立即访问第二次,则很快,毫秒级响应。

注:冷启动虽慢但也不会超过1.5秒,如超过1.5秒应该是云函数写的有问题或网络有问题。

两家云厂商仍然在优化冷启动问题。目前给开发者的建议是:
1. 使用clientDB可以减少遇到冷启动问题的概率
2. 非高频访问的云函数,合并到高频云函数中。也有的开发者使用单路由方式编写云函数,即在一个云函数中通过路由处理实现了整个应用的所有后台逻辑。参考[插件](https://ext.dcloud.net.cn/search?q=%E8%B7%AF%E7%94%B1&cat1=7&orderBy=UpdatedDate)
  但使用这种方式需注意平衡,如果业务代码太多,每次云函数请求产生的内存消耗也会不少。
3. 非高频访问的云函数,可以通过定时任务持续运行它(注意腾讯云可以使用这个方式完全避开冷启动,而阿里云的定时任务最短周期大于资源回收周期)
4. 阿里云支持配置云函数的单实例多并发,请参考:[单实例多并发](cf-functions.md?id=concurrency)
5. 腾讯云付费进行实例预留

### 云函数的无状态

因为存在冷热启动的差异,云函数中的全局变量就可能出现每次不一样的情况。也就是**云函数是无状态的**

以如下代码为例,`count`作为全局变量,当多次调用该云函数时,可能会出现变量累加的情况(实例未复用时,每次返回0,若实例被复用,则可能返回1、2、3等各种意外情况)。所以不要这么使用。


```javascript
let count = 0;
module.exports = async (event) => {
  return count++
  //此示例为错误示例
  //云函数实例未复用时,每次返回0
  //若实例被复用,则可能返回1、2、3等各种意外情况
}
```

**require由于存在缓存,也存在同样的问题。尽量不要直接修改require返回的内容**

695 696 697 698
虽然云函数无状态,但我们也可以通过其他方式来替代全局变量:
- uni-config-center:静态全局变量可以使用uni提供的配置中心。[详见](uni-config-center.md)
- redis:动态变量使用redis。[详见](https://uniapp.dcloud.io/uniCloud/redis-introduction.html)

699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746
### 临时存储空间

云函数是运行在云端的代码,运行环境由云服务器弹性调配,这是和传统`Node.js`应用很大的区别。

换言之,云函数每次执行的宿主环境(可简单理解为虚拟机或服务器硬件)可能相同,也可能不同,因此传统`Node.js`开发中将部分信息存储本地硬盘或内存的方案就不再适合,建议通过云数据库或云存储的方案替代。

### 云函数中的异步行为

书写云函数时应注意`async``await`的使用,`nodejs`有内置模块`util`可以将符合`error-first`形式`callback`的函数转换为`promise`形式,[详情参考](https://nodejs.org/api/util.html#util_util_promisify_original),比如以下示例:

```js
const {
	promisify
} = require('util')

let testCallback = {
	value: 'testCallbackValue',
	echo: function(num, callback) {
		setTimeout(() => {
      // 第一个参数为error,第二个为返回值
			callback(null, `${this.value}:${num}`)
		}, 2000)
	}
}

exports.main = async function() {
  // num=2,不传入callback参数,callback会自动作为回调函数处理
	let val = await promisify(testCallback.echo).call(testCallback, 2)
	console.log(val)
	return val
}

```

如果想在云函数内使用回调形式可以让云函数返回一个promise,如以下示例:

```js
exports.main = async function() {
	return new Promise((resolve, reject) => {
		setTimeout(() => {
			resolve('some return value')
		}, 1000)
	})
}
```


### return的策略@return
W
wanganxp 已提交
747 748 749 750 751 752 753

- 阿里云 return 之后云函数立即终止,逻辑不会继续执行,包括 settimeout 或其他异步操作都会立即终止。
- 腾讯云 node8 return 之后也不会继续执行,但 node12 可以配置是否继续执行
- HBuilderX 本地运行
	* 不通过客户端发起,直接本地运行云函数/云对象,return 之后还可以执行300ms
	* 通过客户端连接本地云函数/云对象,return 之后可以继续执行

W
wanganxp 已提交
754
**腾讯云因为按 GBS 对云函数计费,在 node12 时,尤其要注意,如果未有效终止云函数,会一直计费**
W
wanganxp 已提交
755

756 757 758 759
### 时区

- 云端的云函数中使用的时区是 `UTC+0`,而不是 `UTC+8`,在云函数中使用时间时需特别注意。云函数在HBuilderX本地运行时,时区则是电脑的时区,很可能是 `UTC+8`。建议使用时间戳,可以规避时区问题。

Q
qiang 已提交
760 761
## 云函数配置

762
云函数除了代码,还有配置。在uniCloud web控制台可以配置;在HBuilderX项目中,云函数根目录的`package.json`也是存放配置的地方。
W
wanganxp 已提交
763

雪洛's avatar
雪洛 已提交
764
### 超时时间@timeout
Q
qiang 已提交
765

766
阿里云非定时触发请求云函数最大只支持10秒的超时时间。定时任务触发最大支持600秒的超时时间,一般用于跑批。
Q
qiang 已提交
767

雪洛's avatar
雪洛 已提交
768
腾讯云最大支持900秒超时时间
Q
qiang 已提交
769

770
如果超时时间仍然不够用,可以参考云函数递归调用,连续执行多个云函数处理一个任务[详情查看](uniCloud/cf-functions.md?id=recurrence)
771

Q
qiang 已提交
772 773
### 固定出口IP@eip

W
wanganxp 已提交
774
serverless默认是没有固定的服务器IP的,因为有很多服务器资源在后台供随时调用,每次调用到哪个服务器、哪个ip都不固定。
Q
qiang 已提交
775 776 777 778

但一些三方系统,要求配置固定ip白名单,比如微信公众号的js sdk,此时只能提供固定ip地址。

目前腾讯云的收费版,提供了云函数的固定出口ip。ip属于有价资源,阿里云和腾讯云的免费版不提供这方面的能力。
779
> 如果因此你想要切换云厂商,需要把uniCloud阿里云版中的数据,迁移到腾讯云版。参考:[云厂商之间的迁移](https://uniapp.dcloud.io/uniCloud/hellodb?id=cross-provider)
Q
qiang 已提交
780 781 782 783 784 785 786

在uniCloud [Web控制台](https://unicloud.dcloud.net.cn),创建付费的腾讯云服务空间,选择一个云函数,在云函数的详情界面可以开启固定出口ip。开启后界面上会显示可用的固定ip。拿着这个ip去需要固定ip的界面(如微信公众号管理界面)配置即可。

**注意**

- 如果你是免费版升配到付费版,开启`固定IP`功能后,会导致付费版到期无法自动降级到免费版,请注意按时续费

雪洛's avatar
雪洛 已提交
787 788 789 790 791 792

腾讯云原本的设计是同一个服务空间内所有开启固定出口IP的云函数使用的是同一个IP。但是对于开通vpc的云函数无法和未开通vpc的函数共用同一个出口ip。具体使用中有以下表现

- 开通redis扩展的云函数和未开通redis扩展的云函数会分配不同的ip
- 如果一个云函数已经开通固定出口ip,再关联redis扩展库时固定ip会发生变化

雪洛's avatar
雪洛 已提交
793
建议已开通redis的服务空间先将云函数关联redis扩展再开通固定出口IP,**2022年7月20日起新上传的云函数会默认开启vpc功能,如需旧云函数和新云函数保持一致可以把旧云函数关联redis扩展后上传一次,注意这样操作会改变旧云函数的固定出口IP**
雪洛's avatar
雪洛 已提交
794

Q
qiang 已提交
795 796
### 单实例多并发@concurrency

797
> 仅阿里云支持
Q
qiang 已提交
798 799 800 801 802 803 804 805 806 807 808 809 810 811 812

默认情况下云函数仅支持单实例单并发,即同一时间一个实例仅可为一个用户服务(不同用户同一时间访问会被分派到不同实例进行处理)。通过修改云函数单实例并发度,可以修改云函数同一时间最多能处理多少请求。

假设同时有3个请求需要处理,当实例并发度设置为1时,需要创建3个实例来处理这3个请求,每个实例分别处理1个请求。而每开启一个实例都会引发云函数冷启动;当云函数的实例并发度设置为10时(即1个实例可以同时处理10个请求),只需要创建1个实例就能处理这3个请求。这样后面2个并发请求不会造成云函数的冷启动。

**开启方式**

云函数详情页面配置单实例并发度即可,支持1-100之间的数值

**效果**

- 有效减少并发请求时云函数冷启动次数
  
**使用注意**

813
- 适用于云函数连接三方服务器的场景,如果你的云函数只处理数据库请求,不要修改此配置,保持为1即可
Q
qiang 已提交
814 815
- 云函数内存使用量会随着并发量增大而增加
- 如果并发的不同请求对全局变量同时进行读写会污染全局变量,可能会导致意想不到的后果,开启单实例多并发后请不要编写修改全局变量的代码,除非你熟悉这种技术带来的特殊应用,比如下文进阶部分提到的ip过滤。
816
- 设置过大的单实例多并发可能会导致实例底层网络请求排队从而导致请求超时,**再次强调此项,一般情况下不要设置过大的并发度,具体数值可以自己针对业务代码测试一下**
Q
qiang 已提交
817 818 819

**适用场景**

雪洛's avatar
雪洛 已提交
820 821 822 823 824
|场景									|适用性	|理由																	|
|:-:									|:-:	|:-:																	|
|函数中有较多时间在等待下游服务的响应	|适用	|等待响应一般不消耗资源,在一个实例内并发处理可以节省费用。				|
|函数中有共享状态且不能并发访问			|不适用	|例如全局变量,多请求并发执行修改共享状态可能会导致错误。				|
|单个请求的执行要消耗大量CPU及内存资源	|不适用	|多请求并发执行会造成资源争抢,可能会导致内存不足(OOM)或者延时增加。	|
Q
qiang 已提交
825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860

**关于uni-id的特殊说明**

```js
// 开启单实例多并发前的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
}
```

**进阶**

开启单实例多并发后的全局变量复用并非一定是坏的结果,如果你很了解此行为,也可以对此进行有效的利用

例:[ip-filter](https://ext.dcloud.net.cn/plugin?id=4619)中就利用云函数全局缓存一些ip访问信息来限制单ip访问频率,可以下载示例项目体验一下


## 云函数package.json@packagejson

W
wanganxp 已提交
861
HBuilderX 3.0版本之前,package.json只是一个标准的package.json,安装依赖或公共模块才需要。HBuilderX 3.0及以上版本,package.json也可以用来配置云函数。
Q
qiang 已提交
862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877

uniCloud web控制台提供了很多云函数的设置,比如内存大小、url化、定时触发等,从HBuilderX 3.0起,在云函数的package.json里也可以编写这些设置。

开发者在本地编写云函数的设置,上传云函数,这些设置会自动在云端生效。(本地不生效)

在云端设置了非默认参数后,HBuilderX下载云函数到本地时,也会自动把设置项放入package.json中下载下来。

package.json是一个标准json文件,不可带注释。下面是一个package.json示例。

```json
{
  "name": "add-article",
  "version": "1.0.0",
  "description": "新增文章",
  "main": "index.js",
  "dependencies": {
雪洛's avatar
雪洛 已提交
878
    // 云函数的依赖,包括公共模块及自行安装的npm依赖
Q
qiang 已提交
879
  },
雪洛's avatar
雪洛 已提交
880 881 882
	"extensions": {
		// 云函数使用的扩展库
	},
Q
qiang 已提交
883
  "cloudfunction-config": {
雪洛's avatar
雪洛 已提交
884 885 886 887 888 889 890 891 892 893
		"memorySize": 256,
		"timeout": 5,
		"triggers": [{
				"name": "myTrigger",
				"type": "timer",
				"config": "0 0 2 1 * * *"
		}],
		"path": "",
		"runtime": "Nodejs8" 
	}
Q
qiang 已提交
894 895 896
}
```

雪洛's avatar
雪洛 已提交
897
### cloudfunction-config@cloudfunction-config
Q
qiang 已提交
898

899 900
其中cloudfunction-config字段是云函数配置,支持的配置如下

Q
qiang 已提交
901 902 903 904 905 906
```js
{
  "concurrency": 10, // 单个云函数实例最大并发量,不配置的情况下默认是1
  "memorySize": 256, // 函数的最大可用内存,单位MB,可选值: 128|256|512|1024|2048,默认值256
  "timeout": 5, // 函数的超时时间,单位秒,默认值5。最长为60秒,阿里云在定时触发时最长可以是600秒
  // triggers 字段是触发器数组,目前仅支持一个触发器,即数组只能填写一个,不可添加多个
雪洛's avatar
雪洛 已提交
907
  "triggers": [{ // 阿里云腾讯云均为此形式,请阅读下方说明
Q
qiang 已提交
908 909 910 911 912 913 914 915 916
      // name: 触发器的名字,规则见https://uniapp.dcloud.net.cn/uniCloud/trigger,name不对阿里云生效
      "name": "myTrigger",
      // type: 触发器类型,目前仅支持 timer (即 定时触发器),type不对阿里云生效
      "type": "timer",
      // config: 触发器配置,在定时触发器下,config 格式为 cron 表达式,规则见https://uniapp.dcloud.net.cn/uniCloud/trigger。使用阿里云时会自动忽略最后一位,即代表年份的一位在阿里云不生效
      "config": "0 0 2 1 * * *"
  }],
  // 云函数Url化path部分,阿里云需要以/http/开头
  "path": "",
雪洛's avatar
雪洛 已提交
917 918
  "runtime": "", // nodejs版本,可选Nodejs8、Nodejs12,默认:Nodejs8
  "keepRunningAfterReturn": true // 是否在云函数return之后继续执行,仅腾讯云nodejs12生效,详情见下方说明
Q
qiang 已提交
919 920 921
}
```

雪洛's avatar
雪洛 已提交
922 923
**使用腾讯云Nodejs12版本时,务必仔细阅读此文档:[keepRunningAfterReturn](#keep-running)**

雪洛's avatar
雪洛 已提交
924
#### triggers@triggers
雪洛's avatar
雪洛 已提交
925 926 927 928 929 930 931 932 933 934 935 936 937 938 939

阿里云定时触发的cron表达式不支持代表年的第七位,但是在package.json内配置时仍需将第七位设置为*

**在web控制台配置trigger请参考:[定时触发](uniCloud/trigger.md)**

package.json内统一了腾讯阿里的配置,两个平台都需要配置为如下形式

```js
{
	"name": "myTrigger",
	"type": "timer",
	"config": "0 0 2 1 * * *"
}
```

雪洛's avatar
雪洛 已提交
940 941 942 943 944 945 946 947 948 949 950
#### keepRunningAfterReturn@keep-running

> 新增于HBuilderX 3.5.1

阿里云、腾讯云nodejs8在云函数return之后其余逻辑会被冻结不再执行。腾讯云nodejs12表现恰好相反,云函数return之后还会等待其余逻辑执行后才会将此云函数实例空闲出来。

以下面的代码为例

```js
exports.main = async function(event, context) {
	setTimeout(()=>{
雪洛's avatar
雪洛 已提交
951
	  console.log('delay 5 seconds')
雪洛's avatar
雪洛 已提交
952 953 954 955 956 957 958
	}, 5000)
	return {}
}
```

如果此云函数运行在阿里云或腾讯云nodejs8,setTimeout里面的console.log不会在本次云函数调用执行,但是可能在云函数实例再次被复用时继续执行。

雪洛's avatar
雪洛 已提交
959
如果此云函数运行在腾讯云nodejs12,setTimeout里面的console.log会在本次云函数调用内,同样的本次云函数**计费时间(与云函数GBs指标相关)**也会按照最终执行完成的时间计算(5000ms+return耗时)。但是前端无需等待5秒即可收到响应。注意:如果有未断开的长连接(例如:redis连接)会导致云函数一直运行到配置的超时时间
雪洛's avatar
雪洛 已提交
960

雪洛's avatar
雪洛 已提交
961
当在云函数package.json内的cloudfunction-config内配置了`keepRunningAfterReturn: false`时,可以改变腾讯云nodejs12的表现,云函数return之后将不再继续执行,未断开的长连接也不会增加云函数实际运行时间,云函数return后长连接也不会被中断,简单来说其表现和腾讯云nodejs8一致。
雪洛's avatar
雪洛 已提交
962

雪洛's avatar
雪洛 已提交
963 964 965 966 967 968 969 970 971 972 973 974 975 976
**在云函数中发送网络请求**

将上述示例中的setTimeout换成网络请求、调用其他云函数或数据库请求同理,如果在阿里云或腾讯云nodejs8直接return会导致网络请求可能无法发送(即使成功发送也是在下一次云函数实例被复用的时候),这是与传统开发不太一样的地方。

```js
exports.main = async function(event, context) {
	uniCloud.callFunction({ 
    name: 'test',
    data: {}
  })
	return {} // callFunction后不等待直接return时无法调用到test云函数
}
```

雪洛's avatar
雪洛 已提交
977 978
**腾讯云nodejs12使用redis**

雪洛's avatar
雪洛 已提交
979
由于redis需要和服务器建立连接,此连接会阻止云函数结束执行。如果没有云函数return之后还需要继续执行的需求,可以简单的在`cloudfunction-config`内配置`keepRunningAfterReturn: false`。这样redis的连接并不会中断,下次请求来时依然可以使用之前建立的连接。
雪洛's avatar
雪洛 已提交
980 981 982

如果需要return之后继续执行,那么需要在使用完毕后断开redis连接,调用`redis.quit()`方法即可断开连接。需要注意的是断开连接后之前建立的连接将不再可用,下个请求到来时需要使用`uniCloud.redis()`方法重新建立连接。

雪洛's avatar
雪洛 已提交
983 984 985 986
**如未按照上述说明进行操作,redis连接将会一直占用云函数实例,导致云厂商持续计算云函数执行时间,可能会导致消耗大量云资源而产生额外费用**

**务必确定自己已理解此文档内容,因未按照文档说明使用导致的额外计费DCloud不承担任何责任**

雪洛's avatar
雪洛 已提交
987
### 注意事项
Q
qiang 已提交
988 989 990 991 992 993 994 995

- 插件作者在发布插件时,如果云函数有特殊设置,应该放入package.json中,然后发布到插件市场。这样就不用再通过说明文档一步一步引导用户去配置云函数定时触发器、内存、url化路径等
- 在web控制台修改云函数配置后,通过HBuilderX的下载云函数菜单会在package.json内添加修改后的云函数配置
- 上传云函数时,如果项目下的package.json内包含云函数配置会同时进行云函数的配置更新
- package.json只有云端部署才生效,本地运行不生效。
- cloudfunction-config不可删除云端配置。例:云端已配置triggers(定时触发器),删除cloudfunction-config内的trigger不会删掉云端的定时触发器
- runtime参数(nodejs版本)仅可在创建云函数时生效,不可修改

996

997 998 999 1000 1001 1002
### 云函数的数量、体积、冷启动的平衡

鉴于:
- 每个服务空间的云函数数量是有限的,阿里云是48个,腾讯云是9~149个,[详见](price.md)
- 每个云函数的体积限制是10M(含node_modules)
- 云函数有冷启动问题
1003

1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014
基于以上情况,对开发模式有如下建议:

1. 一般不建议使用体积较大、依赖较深的node_modules。多使用DCloud官方或插件市场提供的库。
2. 优先使用clientDB,不占用云函数数量,也不用编写服务器代码。
3. 对云对象或云函数做适当的合并和拆解。
	- 低频且对用户体验影响较大的操作不建议独立使用云函数,合并到高频云函数中。
	- 控制好单个云函数体积,有的开发者喜欢使用[单路由云函数](https://ext.dcloud.net.cn/search?q=%E8%B7%AF%E7%94%B1&orderBy=WeekDownload&cat1=7),整个服务空间就一个云函数。这也得根据实际情况,如果云函数体积超过6M也还是建议分拆。
	- 用户体系方面,官方已经提供uni-id-co云对象,再搭配clientDB,常规业务就够了。有特殊需求可以再适度补若干云对象。不太会发生云函数数量不足的情况。
4. 必要时可以使用多个服务空间,跨服务空间使用

## cloudfunctions_init(已废弃)
Q
qiang 已提交
1015

W
wanganxp 已提交
1016
`HBuilderX 2.9`版本,`uniCloud`提供了`cloudfunctions_init.json`来方便开发者快速进行云函数的初始化操作。
Q
qiang 已提交
1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079

**注意:HBuilderX 3.0.0版本起不再使用cloudfunctions_init.json来初始化云函数。改为使用在云函数目录下通过package.json进行配置,具体见上个章节**

详细调整如下:

不再使用cloudfunctions_init.json,内容被分散到每个云函数的package.json的`cloudfunction-config`字段下

package.json是一个标准json文件,不可带注释。下面是一个package.json示例

```json
{
  "name": "add-article",
  "version": "1.0.0",
  "description": "新增文章",
  "main": "index.js",
  "dependencies": {
    
  },
  "cloudfunction-config": {
      "memorySize": 256,
      "timeout": 5,
      "triggers": [{
          "name": "myTrigger",
          "type": "timer",
          "config": "0 0 2 1 * * *"
      }],
      "path": ""
    }
}
```

cloudfunction-config说明如下

```js
{
  "memorySize": 256, // 函数的最大可用内存,单位MB,可选值: 128|256|512|1024|2048,默认值256
  "timeout": 5, // 函数的超时时间,单位秒,默认值5。最长为60秒,阿里云在定时触发时最长可以是600秒
  // triggers 字段是触发器数组,目前仅支持一个触发器,即数组只能填写一个,不可添加多个
  "triggers": [{
      // name: 触发器的名字,规则见https://uniapp.dcloud.net.cn/uniCloud/trigger,name不对阿里云生效
      "name": "myTrigger",
      // type: 触发器类型,目前仅支持 timer (即 定时触发器),type不对阿里云生效
      "type": "timer",
      // config: 触发器配置,在定时触发器下,config 格式为 cron 表达式,规则见https://uniapp.dcloud.net.cn/uniCloud/trigger。使用阿里云时会自动忽略最后一位,即代表年份的一位在阿里云不生效
      "config": "0 0 2 1 * * *"
  }],
  // 云函数Url化path部分,阿里云需要以/http/开头
  "path": ""
}
```

**HBuilderX 3.0.0之前版本,请继续阅读下面文档**

**使用方式**
-`cloudfucntions`目录右键即可创建`cloudfunctions_init.json`
- 编写好json内容,在`cloudfunctions_init.json`上右键初始化云函数配置。

**cloudfunctions_init.json形式如下**

```json
{
    "fun-name": { // 云函数名称
        "memorySize": 256, // 函数的最大可用内存,单位MB,可选值: 128|256|512|1024|2048,默认值256
雪洛's avatar
雪洛 已提交
1080
        "timeout": 5, // 函数的超时时间,单位秒,默认值5
Q
qiang 已提交
1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095
        // triggers 字段是触发器数组,目前仅支持一个触发器,即数组只能填写一个,不可添加多个
        "triggers": [{
            // name: 触发器的名字,规则见https://uniapp.dcloud.net.cn/uniCloud/trigger,name不对阿里云生效
            "name": "myTrigger",
            // type: 触发器类型,目前仅支持 timer (即 定时触发器),type不对阿里云生效
            "type": "timer",
            // config: 触发器配置,在定时触发器下,config 格式为 cron 表达式,规则见https://uniapp.dcloud.net.cn/uniCloud/trigger。使用阿里云时会自动忽略最后一位,即代表年份的一位在阿里云不生效
            "config": "0 0 2 1 * * *"
        }],
        // 云函数Url化path部分,阿里云需要以/http/开头
        "path": ""
    }
}

```