最佳实践

有奖征文|投稿上云技术实践,赢取价值5000元大奖> HOT

函数运行时的实例模型

云函数 SCF 将在函数接收到触发请求时为您执行函数。SCF 执行请求的资源为实例,根据函数的配置信息(如内存大小等)进行资源分配,并启动一个或多个实例处理函数请求。SCF 平台负责所有函数运行实例的创建、管理和删除清理操作,用户没有权限对其进行管理。

实例的生命周期如下图所示:

实例启动

  • 如果请求到来时没有正在运行的实例,请求会触发实例的启动。实例启动通常需要一些时间,这会使触发实例启动的调用增加一些耗时。通常首次调用函数、更新函数或长时间未调用时重新调用函数时才会触发实例启动。
  • 实例启动耗时将体现在函数 执行日志 Init ReportProvisioned Report 行中的 Coldstart 字段中。
  • 您可以使用 预置并发 功能提前完成实例启动,避免函数请求到来时再触发实例拉起。
  • 实例启动耗时受函数 初始化超时时间 限制,若实例启动所需时间大于函数配置的初始化超时时间,将会导致实例启动失败。请参考下文优化实例启动耗时或适当调大初始化超时时间配置。

实例启动三个子阶段及耗时优化方法:

  • 代码准备:平台拉取用户上传的函数代码、层或镜像,为函数执行做准备。代码准备耗时与代码包、层、镜像大小成正相关,建议尽可能的精简代码包大小,只保留函数执行必须的代码文件及依赖,以获取最短的代码准备耗时。代码准备耗时将体现在函数 执行日志 Init ReportProvisioned Report 行中的 PullCode 字段中。
  • 运行时初始化:平台按照用户的函数配置准备函数执行依赖的运行环境。运行时初始化耗时将体现在函数 执行日志Init ReportProvisioned Report 行中的 InitRuntime 字段中。
  • 函数代码初始化:代码初始化耗时与代码逻辑复杂程度成正相关,建议尽可能优化代码逻辑,以获取最短的代码初始化耗时。函数代码初始化耗时将体现在函数 执行日志Init ReportProvisioned Report 行中的 InitFunction 字段中。
    • 代码部署的事件函数:实例启动阶段将会运行函数配置的 执行方法 之前的代码逻辑。
    • 代码部署的 Web 函数:实例启动阶段将会运行启动文件 scf_bootstrap 和监听 9000 端口之前的代码逻辑。
    • 镜像部署的函数:事件函数将会运行函数配置的 执行方法 之前的代码逻辑;Web 函数将会运行启动文件 scf_bootstrap 和监听 9000 端口之前的代码逻辑。

实例复用

为尽量减少实例启动带来的耗时,平台会尝试对后续调用复用实例。在实例处理完函数请求后会根据平台的实际情况存留一段时间以用于下次调用,在此时间段内的调用会优先复用该实例。

实例复用的意义在于:

  • 用户代码中位于 执行方法 外部的任何声明保持已初始化的状态,再次调用函数时可以直接重用。例如,如果您的函数代码中建立了数据库连接,容器重用时可以直接使用原始连接。您可以在代码中添加逻辑,在创建新连接之前检查是否已存在连接。
  • 每个容器在 /tmp 目录中提供部分磁盘空间。容器存留时该目录内容会保留,提供可用于多次调用的暂时性缓存。再次调用函数时有可能可以直接使用该磁盘内容,您可以添加额外的代码来检查缓存中是否有您存储的数据。
    注意:

    请勿在函数代码中假定始终重用实例,因为是否重用和单次实际调用相关,无法保证是创建新实例还是重用现有实例。

实例回收

平台会对一段时间内没有处理请求的实例进行回收。

临时磁盘空间

SCF 函数在执行过程中,都拥有一块 512MB 的临时磁盘空间 /tmp,用户可以在执行代码中对该空间进行一些读写操作,也可以创建子目录,但这部分数据可能不会在函数执行完成后保留。因此,如果您需要对执行过程中产生的数据进行持久化存储,请使用 对象存储 COS 或 Redis/Memcached 等外部持久化存储。

调用类型

SCF 平台支持同步和异步两种调用方式来调用云函数。

同步调用

同步调用函数将会在调用请求发出后持续等待函数的执行结果返回。

异步调用

  • 异步调用将不会等待结果返回,只发出请求并获得当前请求的 Request ID。
  • 当发生异步调用时,异步事件会先放置到云函数内置的异步队列,然后再消费异步队列内的事件执行函数。异步队列有如下限制:
    • 异步队列是触发器维度的,一个函数触发器一个队列。
    • 异步事件在队列中的保留时长最大 6 小时。
    • 异步队列的长度上限为 10 万条消息。
  • 异步调用的重试策略也有所不同,详情请参见 重试策略

定义函数调用类型

调用类型与函数本身的配置无关,只有在调用函数时才能控制调用类型。

以下调用场景您可以自由定义函数的调用类型:

  • 编写的应用程序调用 SCF 函数。如果您需要同步调用,请使用同步调用接口 InvokeFunction;如果您需要异步调用,请使用 Invoke 接口,并传入参数 invokeType=Event
  • 手动调用 SCF 函数(使用 API 或 CLI)用于测试。调用时的参数区别同上。

当您使用腾讯云其他云服务作为事件源时,云服务的调用类型是预定义的:

用户限制

函数相关的使用配额及相关环境限制,可见 配额及限制

函数并发量

函数的并发数量是指在任意指定时间对函数代码的执行数量。对于当前的 SCF 函数来说,每个发布的事件请求就会执行一次。因此,这些触发器发布的事件数(即请求量)会影响函数的并发数。您可以使用以下公式来估算并发的函数实例总数目。

每秒请求量 * 函数执行时间(按秒) 

例如,考虑一个处理 COS 事件的函数,假定函数平均用时 0.2 秒(即 200 毫秒),COS 每秒发布 300 个请求至函数。这样将同时生产 300 * 0.2 = 60 个函数实例。

并发限制

当前默认情况下,SCF 对每个函数的并发量有一定限制,您可以通过查看 并发管理 了解当前函数的并发量限制。
如果调用导致函数的并发数目超过了默认限制,则该调用会被阻塞,SCF 将不会执行这次调用。根据函数的调用方式,受限制的调用的处理方式会有所不同:

  • 同步调用:如果函数被同步调用时受到限制,将会直接返回 432 错误码
  • 异步调用:如果函数被异步调用时受到限制,SCF 将以一定的策略 重试 受限制的事件。

执行环境和可用库

当前 SCF 的执行环境建立在以下基础上:

  • 标准 CentOS 7.2

如果需要在代码中包含可执行的二进制文件、动态库或静态库,请都确保兼容此执行环境。
基于不同语言环境,在 SCF 执行环境下有相关语言的基础库及安装的附加库,您可以在各个语言说明中查看环境中已安装的附加库:

部署方式

云函数(Serverless Cloud Function,SCF)提供代码部署、镜像部署两种部署方式,支持事件函数和 Web 函数两种函数类型。不同的部署方式以及函数类型在代码开发时需要采用不同的规范。本文主要介绍代码部署的事件函数的编写规范及相关概念,镜像部署Web 函数 详情请参考对应文档。

SCF 事件函数

SCF 事件函数有三个基本概念:执行方法、函数入参和函数返回。
上述概念在通常的项目开发中分别对应:

  • 执行方法:对应项目的主函数,是程序执行的起点。
  • 函数入参:即通常理解的函数入参,但在云函数环境下,入口函数的入参为平台固定值,详情见 函数入参
  • 函数返回:对应项目中主函数的返回值,函数返回后,代码执行结束。

执行方法

SCF 平台在调用云函数时,首先会寻找执行方法作为入口,执行用户的代码。此时,用户需以文件名.执行方法名的形式进行设置。
例如,用户设置的执行方法为 index.handler,则 SCF 平台会首先寻找代码程序包中的 index 文件,并找到该文件中的 handler 方法开始执行。
在执行方法中,用户可对入口函数入参进行处理,也可任意调用代码中的其他方法。SCF 的某个函数以入口函数执行完成或函数执行异常作为执行结束。

函数入参

函数入参,是指函数在被触发调用时所传递给函数的内容。通常情况下,函数入参包括eventcontext两部分,但根据开发语言和环境的不同,入参个数可能有所不同,详情请参见 开发语言说明

作用

event 参数类型为 dict,event 中包含了触发函数执行的基本信息,可以是平台定义的格式,也可以自定义格式。函数被触发开始执行后,可以在代码内部对 event 进行处理。

使用说明

有两种方法可以触发云函数 SCF 执行:

  1. 通过调用 云 API 触发函数执行。
  2. 通过绑定 触发器 触发函数执行。

SCF 两种触发方式对应两种 event 格式:

  • 云 API 触发函数执行:
    可以在调用方和函数代码之间自定义一个 dict 类型的参数。调用方按照定义好的格式传入数据,函数代码按格式获取数据。
    示例
    定义一个 dict 类型的数据结构 {"key":"XXX"} ,当调用方传入数据 {"key":"abctest"} 时,函数代码可以通过 event [key] 来获得值 abctest。

  • 触发器触发函数执行:
    SCF 和 API 网关、对象存储 COS、消息队列 Ckafka 等多种云服务打通,可以通过给函数绑定对应的云服务触发器触发函数执行。触发器触发函数时,event 会以一种平台预定义的、不可更改的格式作为 event 参数传给函数,您可以根据此格式编写代码并从 event 参数中获取信息。
    示例
    通过对象存储 COS 触发函数时会将对象存储桶及文件的具体信息以 JSON 格式 传递给 event 参数,在函数代码中通过解析 event 信息即可完成对触发事件的处理。

了解 event 入参和 context 入参的基本用法后,在编写函数代码时您还需注意以下几点:

  • 为保证针对各开发语言和环境的统一性,event 入参和 context 入参均使用 JSON 数据格式统一封装。
  • 不同触发器在触发函数时,所传递的数据结构均有所不同。详情请参见 函数触发器说明
  • 当云函数不需要任何输入时,您可以在代码中忽略 event 和 context 参数。

函数返回

SCF 平台会获取到云函数执行完成后的返回值,并根据下表中不同的触发方式进行处理。

触发方式 处理方式
同步触发
  • 通过 API 网关、云 API 同步 invoke 触发函数的方式为同步触发。
  • 使用同步方式触发的函数在执行期间,SCF 平台不会返回触发结果。
  • 在函数执行完成后,SCF 平台会将函数返回值封装为 JSON 格式并返回给调用方。
异步触发
  • 使用异步方式触发的云函数,SCF 平台接收触发事件后,会返回触发请求 ID 。
  • 在函数执行完成后,函数的返回值会封装为 JSON 格式并存储在日志中。
  • 用户可在函数执行完成后,通过返回的请求 ID 查询日志获取该异步触发函数的返回值。

当函数中的代码返回具体值时,通常返回特定的数据结构。例如 :

运行环境 返回数据结构类型
Python 简单数据结构或 dict 数据结构
Node.js JSON Object
PHP Array 结构
GO 简单的数据结构或带有 JSON 描述的 struct

为保证针对各开发语言和环境的统一性,函数返回会使用JSON 数据格式统一封装。SCF 平台在获取到例如以上运行环境函数的返回值后,将会对返回的数据结构进行 JSON 化,并返回 JSON 内容到调用方。

注意:

  • 需确保函数的返回值均可被 JSON 化,若直接返回对象且不具备 JSON 化方法,将会导致 SCF 平台在 JSON 化操作时失败并报错。
  • 例如以上运行环境的返回值,无需在 return 前自行 JSON 化,否则会导致输出的字符串会进行再次 JSON 化。

异常处理

若函数在调试和运行过程中出现异常,SCF 平台会尽最大可能捕获异常并将异常信息写入日志中。函数运行产生的异常包括捕获的异常(Handled error)和未捕获的异常(Unhandled Error)。

处理方式

您可以前往 SCF 控制台 按照以下步骤进行异常处理测试:

  1. 新建函数并复制以下函数代码,不添加任何触发器。
  2. 单击控制台测试,选择 “Hello World” 测试示例进行测试。

本文提供以下三种抛出异常方式,您可根据实际需求选择在代码中进行异常处理。

示例

def always_failed_handler (event,context):
    raise Exception ('I failed!')
说明
此函数在运行过程中将引发异常,返回以下错误信息,SCF 平台会将此错误信息记录到函数日志中。
File "/var/user/index.py", line 2, in always_failed_handler
raise Exception ('I failed!')
Exception: I failed!

返回错误信息

当用户的代码逻辑中未进行异常处理及错误捕获时,SCF 平台会尽可能的捕获错误。例如,用户函数在运行过程中突然崩溃退出,当出现此类平台也无法捕获错误的情况时,系统将会返回一个通用的错误信息。
下表展示了代码运行中常见的一些错误:

错误场景 返回消息
使用 raise 抛出异常 {File "/var/user/index.py", line 2, in always_failed_handler raise Exception ('xxx') Exception: xxx}
处理方法不存在 {'module' object has no attribute 'xxx'}
依赖模块并不存在 {global name 'xxx' is not defined}
超时 {"time out"}

日志

SCF 平台会将函数调用的所有记录及函数代码中的全部输出存储在日志中,请使用编程语言中的打印输出语句或日志语句生成输出日志,方便调试及故障排除时使用。详情见 日志管理

注意事项

由于 SCF 的特点,您须以无状态的风格编写您的函数代码。本地文件存储等函数生命周期内的状态特征,在函数调用结束后将随之销毁。
因此,持久状态建议存储在关系型数据库 TencentDB、对象存储 COS、云数据库 Memcached 或其他云存储服务中。

开发流程

了解更多云函数开发流程,请参见 使用流程

目录