LCEngine.run("averageStars", parameters: ["movie": "夏洛特烦恼"]) { (result) in
switch result {
case .success(value: let resultValue):
print(resultValue)
case .failure(error: let error):
print(error)
}
}
var paramsJson = {
movie: "夏洛特烦恼"
};
AV.Cloud.run('averageStars', paramsJson).then(function (data) {
// 处理结果
}, function (err) {
// 处理报错
});
from leancloud import cloudfunc
cloudfunc.run('averageStars', movie='夏洛特烦恼')
LCEngine.call("averageStars", parameters: ["movie": "夏洛特烦恼"]) { (result) in
switch result {
case .success(object: let object):
if let object = object {
print(object)
}
case .failure(error: let error):
print(error)
}
}
var paramsJson = {
movie: "夏洛特烦恼"
};
AV.Cloud.rpc('averageStars', paramsJson).then(function (object) {
// 处理结果
}, function (error) {
// 处理报错
});
from leancloud import cloudfunc
cloudfunc.rpc('averageStars', movie='夏洛特烦恼')
// 构建参数
Map<String, String> dicParameters = new HashMap<String, String>();
dicParameters.put("movie", "夏洛特烦恼");
AVCloud.callRPCInBackground("averageStars", dicParameters).subscribe(new Observer<AVObject>() {
@Override
public void onSubscribe(Disposable disposable) {
}
@Override
public void onNext(AVObject avObject) {
// succeed.
}
@Override
public void onError(Throwable throwable) {
// failed
}
@Override
public void onComplete() {
}
});
客户端收到的响应:{ "code": 211, "error": "Could not find the user." }。
Cloud::define("customErrorCode", function($params, $user) {
throw new FunctionError("自定义错误信息。", 123);
});
客户端收到的响应:{ "code": 123, "error": "自定义错误信息。" }。
云函数超时
云函数超时时间为 15 秒,如果超过阈值,客户端将收到 HTTP status code 为 503 的响应,body 为 The request timed out on the server。服务端会出现类似这样的日志:WARNING: [pool www] child ... exited on signal 9 (SIGKILL) after ... seconds from start。
除了 503 错误外,有些情况下客户端也可能收到其他报错,如 524 或 141。
对于 before 类的 Hook(也包括 onLogin),如果返回了一个错误的话,这个操作就会被中断,你可以在这些 Hook 中主动抛出一个错误来拒绝掉某些操作。对于 after 类的 Hook(也包括 onVerified),返回错误并不会影响操作的执行(因为其实操作已经执行完了)。
graph LR
A((save)) -->D{object}
D-->E(new)
E-->|beforeSave|H{error?}
H-->N(No)
N-->B[create new object on the cloud]
B -->|afterSave|C((done))
H-->Y(Yes)
Y-->Z((interrupted))
D-->F(existing)
F-->|beforeUpdate|I{error?}
I-->Y
I-->V(No)
V-->G[update existing object on the cloud]
G-->|afterUpdate|C
graph LR
A((delete))-->|beforeDelete|H{error?}
H-->Y(Yes)
Y-->Z((interrupted))
H-->N(No)
N-->B[delete object on the cloud]
B -->|afterDelete|C((done))
在删除一个对象之前做一些检查工作,比如在删除一个相册 Album 前,先检查一下该相册中还有没有照片 Photo:
Cloud::beforeDelete("Album", function($album, $user) {
$query = new Query("Photo");
$query->equalTo("album", $album);
try {
$count = $query->count();
} catch (CloudException $ex) {
throw new FunctionError("An error occurred when getting photo count: {$ex->getMessage()}");
}
if ($count > 0) {
// delete 操作会被丢弃
throw new FunctionError("Cannot delete an album if it still has photos in it.");
}
});
云函数开发指南 · PHP
云函数是云引擎(LeanEngine)的一个子模块,请确保在阅读本文档之前已经阅读了 云引擎服务概览。
当你开发移动端应用时,可能会有下列需求:
这时,你可以使用云引擎的云函数,云函数是一段部署在服务端的代码,编写 JavaScript、Python、PHP 或 Java 代码,并部署到我们的平台上,可以很好地完成上述需求。
如果还不知道如何创建云引擎项目、本地调试并部署到云端,请阅读 云引擎快速入门。
多语言支持
云引擎支持多种语言的运行环境,你可以选择自己熟悉的语言开发应用:
切换云引擎环境
云引擎应用有「生产环境」和「预备环境」之分。在云引擎通过 SDK 调用云函数时,包括显式调用以及隐式调用(由于触发 hook 条件导致 hook 函数被调用),SDK 会根据云引擎所属环境(预备、生产)调用相应环境的云函数。例如,假定定义了
beforeDelete
云函数,在预备环境通过 SDK 删除一个对象,会触发预备环境的beforeDelete
hook 函数。在云引擎以外的环境通过 SDK 显式或隐式调用云函数时,
X-LC-Prod
的默认值一般为1
,也就是调用生产环境。但由于历史原因,各 SDK 的具体行为有一些差异:你还可以在 SDK 中指定客户端将请求所发往的环境:
免费版云引擎 应用只有「生产环境」,因此请不要切换到预备环境。
云函数
示例项目 中
src/cloud.php
文件定义了一个很简单的hello
云函数。在云端进行计算的一个重要理由是,你不需要将大量的数据发送到设备上做计算,而是将这些计算放到服务端,并返回结果这一点点信息就好。现在让我们看一个较复杂的例子来展示云引擎的用途。
例如,你写了一个应用,让用户对电影评分,一个评分对象大概是这样:
stars
表示评分,1-5。如果你想查找《夏洛特烦恼》这部电影的平均分,你可以找出这部电影的所有评分,并在设备上根据这个查询结果计算平均分。但是这样一来,尽管你只是需要平均分这样一个数字,却不得不耗费大量的带宽来传输所有的评分。通过云引擎,我们可以简单地传入电影名称,然后返回电影的平均分。云函数接收 JSON 格式的请求对象,我们可以用它来传入电影名称。整个 LeanStorage PHP SDK 都在云引擎运行环境上有效,可以直接使用,所以我们可以使用它来查询所有的评分。结合在一起,实现
averageStars
函数的代码如下:PHP SDK 提供了
Cloud::start
函数,可以方便快捷地初始化云函数服务。例如,一个专门提供云函数服务的云引擎项目的index.php
:参数和返回值
传递给云函数的参数依次为:
$params: array
:客户端发送的参数。$user: User
:客户端所关联的用户(根据客户端发送的X-LC-Session
头)。$meta: array
:有关客户端的更多信息,目前只有一个$meta['remoteAddress']
属性表示客户端的 IP。SDK 调用云函数
LeanCloud 各个语言版本的 SDK 都提供了调用云函数的接口:
通过 REST API 调用云函数
请查看我们的 云引擎 REST API 使用指南。
云引擎调用云函数
在云引擎中可以使用
LeanCloudEngineCloud::run
调用LeanCloudEngineCloud::define
定义的云函数:云引擎中默认会直接进行一次本地的函数调用,而不是像客户端一样发起一个 HTTP 请求。PHP 云引擎暂不支持发起 HTTP 请求来调用云函数。
RPC 调用云函数
RPC 调用云函数是指:云引擎会在这种调用方式下自动为 HTTP Response Body 做序列化,而 SDK 调用之后拿回的返回结果就是一个完整的
LeanObject
或包含这样的对象的数据结构:云函数错误响应码
可以根据 HTTP status codes 自定义错误响应码。
客户端收到的响应:
{ "code": 211, "error": "Could not find the user." }
。客户端收到的响应:
{ "code": 123, "error": "自定义错误信息。" }
。云函数超时
云函数超时时间为 15 秒,如果超过阈值,客户端将收到 HTTP status code 为
503
的响应,body 为The request timed out on the server
。服务端会出现类似这样的日志:WARNING: [pool www] child ... exited on signal 9 (SIGKILL) after ... seconds from start
。 除了503
错误外,有些情况下客户端也可能收到其他报错,如524
或141
。Hook 函数
Hook 函数本质上是云函数,但它有固定的名称,定义之后会 由系统 在特定事件或操作(如数据保存前、保存后,数据更新前、更新后等等)发生时 自动触发,而不是由开发者来控制其触发时机。需要注意:
_Installation
表暂不支持 Hook 函数。对于
before
类的 Hook(也包括onLogin
),如果返回了一个错误的话,这个操作就会被中断,你可以在这些 Hook 中主动抛出一个错误来拒绝掉某些操作。对于after
类的 Hook(也包括onVerified
),返回错误并不会影响操作的执行(因为其实操作已经执行完了)。为了认证 Hook 调用者的身份,我们的 SDK 内部会确认请求确实是从云引擎内网的云存储组件发出的,如果认证失败,可能会出现
Hook key check failed
的提示,如果在本地调试时出现这样的错误,请确保是通过命令行工具启动调试的。beforeSave
在创建新对象之前,可以对数据做一些清理或验证。例如,一条电影评论不能过长,否则界面上显示不开,需要将其截断至 140 个字符:
afterSave
在创建新对象后触发指定操作,比如当一条留言创建后再更新一下所属帖子的评论总数:
再如,在用户注册成功之后,给用户增加一个新的属性
from
并保存:beforeUpdate
在更新已存在的对象前执行操作,这时你可以知道哪些字段已被修改,还可以在特定情况下拒绝本次修改:
对
$review
直接进行的修改不会被保存。如需拒绝修改,可以让函数返回一个错误。传入的对象是一个尚未保存到数据库的临时对象,并不保证与最终储存到数据库的对象完全相同,这是因为修改中可能包含自增、数组增改、关系增改等原子操作。
afterUpdate
本 Hook 使用不当可能会造成死循环,导致数据存储 API 的调用次数暴涨,甚至产生更多的费用。因此请仔细阅读 防止死循环调用 部分,做出必要的调整和预防措施。
在更新已存在的对象后执行特定的动作。和
beforeUpdate
一样,你可以知道哪些字段已被修改。防止死循环调用
你也许会好奇为什么可以在
afterUpdate
中保存post
而不会再次触发该 hook。这是因为云引擎对所有传入LeanObject
的对象做了处理,以阻止死循环调用的产生。不过请注意,以下情况还需要开发者自行处理:
LeanObject
对象进行fetch
操作。LeanObject
对象,如使用LeanObject::create()
方法。对于使用上述方式产生的对象,请根据需要自行调用
LeanObject->disableBeforeHook()
或LeanObject->disableAfterHook()
:beforeDelete
在删除一个对象之前做一些检查工作,比如在删除一个相册
Album
前,先检查一下该相册中还有没有照片Photo
:afterDelete
在一个对象后被删执行操作,例如递减计数、删除关联对象等等。同样以相册为例,这次我们不在删除相册前检查是否还有照片,而是在删除后,同时删除相册中的照片:
onVerified
当用户通过邮箱或者短信验证时,对该用户执行特定操作。比如:
函数的第一个参数是验证类型。短信验证为
sms
,邮箱验证为email
。另外,数据库中相关的验证字段,如emailVerified
不需要修改,系统会自动更新。onLogin
在用户登录之时执行指定操作,比如禁止在黑名单上的用户登录:
即时通讯 Hook 函数
请阅读 即时通讯概览 · 云引擎 Hook 来了解以下函数的相关参数和用法。
_messageReceived
在消息达到服务器、群组成员已解析完成、发送给收件人之前触发。例如,提前过滤掉聊天内容中的一些广告类的关键词:
_receiversOffline
在消息发送完成时触发、对话中某些用户却已经下线,此时可以根据发送的消息来生成离线消息推送的标题等等。例如截取所发送消息的前 6 个字符作为推送的标题:
_messageSent
消息发送完成之后触发,例如消息发送完后,在云引擎中打印一下日志:
_conversationStart
创建对话,在签名校验(如果开启)之后、实际创建之前触发。例如对话创建时,在云引擎中打印一下日志:
_conversationStarted
创建对话完成触发。例如对话创建之后,在云引擎打印一下日志:
_conversationAdd
向对话添加成员,在签名校验(如果开启)之后、实际加入之前,包括主动加入和被其他用户加入两种情况都会触发。注意如果在创建对话时传入了其他用户的
clientId
作为成员,则不会触发该 hook。例如在云引擎中打印成员加入时的日志:_conversationRemove
从对话中踢出成员,在签名校验(如果开启)之后、实际踢出之前触发,用户自己退出对话不会触发。例如在踢出某一个成员时,在云引擎日志中打印出该成员的
clientId
:_conversationUpdate
修改对话名称、自定义属性,设置或取消对话消息提醒,在实际修改之前触发。例如在更新发生时,在云引擎日志中打印出对话的名称:
Hook 函数错误响应码
为
beforeSave
这类的 hook 函数定义错误码,需要这样:客户端收到的响应为
Cloud Code validation failed. Error detail: { "code": 123, "message": "An error occurred." }
。可通过 截取字符串 的方式取出错误信息,再转换成需要的对象。Hook 函数超时
Before 类 Hook 函数的超时时间为 10 秒,其他类 Hook 函数的超时时间为 3 秒。如果 Hook 函数被其他的云函数调用(比如因为保存对象而触发
beforeSave
和afterSave
),那么它们的超时时间会进一步被其他云函数调用的剩余时间限制。例如,如果一个
beforeSave
函数是被一个已经运行了 13 秒的云函数触发,那么beforeSave
函数就只剩下 2 秒的时间来运行。同时请参考 云函数超时及处理方案。定时任务
定时任务可以按照设定,以一定间隔自动完成指定动作,比如半夜清理过期数据,每周一向所有用户发送推送消息等等。定时任务的最小时间单位是 秒,正常情况下时间误差都可以控制在秒级别。
定时任务是普通的云函数,也会遇到 超时问题,具体请参考 超时处理方案。
一个定时器如果在 24 小时内收到了超过 30 次的
400
(Bad Request)或502
(Bad Gateway)的应答,它将会被云引擎禁用,同时系统会向开发者发出相关的禁用通知邮件。在控制台的日志中,对应的错误信息为timerAction short-circuited and no fallback available
。部署云引擎之后,进入 控制台 > 云引擎 > 定时任务,点击 创建定时器,然后设定执行的函数名称、执行环境等等。例如定义一个打印循环打印日志的任务
logTimer
:定时器创建后,其状态为 未运行,需要点击 启用 来激活。之后其执行日志可以在 日志 页面中查看。
定时任务分为两类:
以 Cron 表达式为例,比如每周一早上 8 点准时发送推送消息给用户:
创建定时器的时候,选择 Cron 表达式 并填入
0 0 8 ? * MON
。Cron 表达式
Cron 表达式的基本语法为:
, - * /
, - * /
, - * /
, - * ? /
, - * /
, - ? /
特殊字符的用法:
*
<分钟>
设为*
,表示每一分钟。?
<日期>
设为10
,<星期>
设为?
。-
<小时>
为10-12
,即 10 点、11 点、12 点。,
<星期>
为MON,WED,FRI
,即周一、周三、周五。/
<秒>
设为0/15
,即从 0 秒开始,以 15 秒为增量,包括 0、15、30、45 秒;5/15
即 5、20、35、50 秒。*/
与0/
等效,如<日期>
设为1/3
,即从每个月的第一天开始,每 3 天(即每隔 2 天)执行一次任务。各字段以空格或空白隔开。
JAN-DEC
、SUN-SAT
这些值不区分大小写,比如MON
和mon
效果一样。举例如下:
0 */5 * * * ?
10 */5 * * * ?
0 30 10-13 ? * WED,FRI
0 */30 8-9 5,20 * ?
定时器数量
生产环境和预备环境的定时器数量都限制在 6 个以内,也就是说你总共最多可以创建 12 个定时器。
错误信息
定时器执行后的日志会记录在 控制台 > 云引擎 > 日志 中,以下为常见的错误信息及原因:
某个定时器触发的云函数,因 15 秒内没有响应而超时(可参考 对云函数调用超时的处理)。
某个定时器触发的云函数因为太多次超时而停止触发。
Master Key 和超级权限
因为云引擎运行在可信的服务器端环境中,所以你可以全局开启超级权限(Master Key),这样云端会跳过包括 ACL 和 Class 权限在内的检查,让你自由地操作所有云存储中的数据,当然这种方式也允许调用一些仅供 Master Key 使用的 API。开启 Master Key 的方法如下:
关于云引擎上的权限问题,还可以参考 ACL 权限管理开发指南 和 在云引擎中使用 ACL。