函数计算在处理用户请求时,自动分配一个或多个实例,每个实例提供一个安全和隔离的运行时环境。传统应用迁移至Serverless架构时,由于函数实例的创建和销毁具有瞬态性,可能导致监控数据更新不及时、指标数据延迟或丢失等问题,为了解决以上痛点,函数计算基于实例生命周期增加多种回调操作,确保监控数据的实时性和完整性。
函数实例的生命周期
函数实例会根据函数当前的请求调用量动态地按需构建与销毁,每个函数实例的生命周期包括实例构建(Creating)、请求调用(Invoke)和实例销毁(Destroy)三个阶段,如下图所示。
实例构建(Creating)
实例构建是指函数计算根据您的函数配置为您创建函数实例。在实例构建阶段,函数计算会按照顺序执行以下三项任务:
实例创建(Instance Create),包括加载代码、层加载或拉取镜像和启动实例。
运行时启动(Runtime Init)。
执行函数配置的Initializer回调(Init Hook)。更多信息,请参见Initializer回调。
实例构建阶段示意图如下所示。
实例构建一般在以下两种情况下发生。
弹性扩容
收到调用请求时,若当前的函数实例已经满载,会构建新的实例来处理请求,并立即执行实例构建流程,紧接着就会执行请求调用流程。弹性扩容可能会造成冷启动,解决方法请参见函数计算冷启动优化最佳实践。
预留配置调整
如果您配置了预留实例,对于这些预留实例,函数计算立即启动实例构建流程。如果当前未收到调用请求,则后续的请求调用流程会与实例构建流程相隔较长时间。更多信息,请参见配置预留实例。
对于一个函数实例,函数配置的Initialize回调方法只会执行一次,如果执行失败,会重新启动一个函数实例执行Initializer流程,Initializer回调失败的实例会直接销毁。
请求调用(Invoke)
实例运行期间,会调用您的函数处理程序以处理来自内外部的函数调用请求。在调用阶段,对于函数计算支持的标准运行时,一个实例在同一时间只会处理一个请求;对于自定义运行时或自定义容器运行时,一个实例在同一时间可以处理多个请求。您可以通过设置单实例多并发实现,具体操作,请参见设置单实例并发度。
函数计算只在实际请求和回调程序执行时计费,在请求以外的时间段内实例会被冷冻,因此不计费,详情请参见计费说明。
实例销毁(Destroy)
如果函数实例在一段时间内没有收到任何调用,则触发此阶段。在销毁阶段,函数计算会先执行PreStop回调方法。您可以在PreStop回调方法中执行一些清理任务。
实例销毁一般在以下三种情况下发生。
实例闲置:如果实例在一段时间内没有收到任何调用请求,函数计算会自动回收该实例。
预留配置调整:当您缩减预留实例数量时,函数计算会立即为您销毁多余的实例。
实例异常:如果实例在构建或运行阶段出现了异常,函数计算会销毁该实例。
实例冻结机制
在没有调用请求时,函数计算会将实例冻结(Freeze),当新的请求来到时,函数计算会将实例解冻(Thaw)。如下图所示。
实例冻结主要发生在以下两种情况。
实例初始化阶段完成后,调用阶段前。
函数某一次调用阶段结束后,下一次调用阶段前。
在一次调用阶段完成后,函数计算将冻结函数实例,程序中的后台进程、线程或协程无法继续运行,异步日志也可能没有写入成功。
使用限制
所有运行时均支持Initialize和PreStop回调方法。
PreStop回调方法的输入参数没有event参数。
PreStop回调函数无返回值,在函数末尾增加返回逻辑无效。
如果使用Java Runtime,您需要将fc-java-core更新至1.4.0及以上版本,否则无法使用PreStop扩展回调函数。
当函数执行返回时,函数计算将冻结函数实例,用户不可假设调用返回时所有异步进程、线程、协程等执行完成,也不可假设本次异步写入的日志被刷新。
计费说明
实例生命周期回调方法的请求数不计费,其他费用与实例调用阶段的计费逻辑相同。计费时长的统计如下图所示。关于计费方式的具体信息,请参见计费概述。
前提条件
已完成函数的创建,具体请参见创建函数。
配置实例生命周期回调方法
通过控制台配置回调
当您使用控制台创建函数时,函数计算不支持配置PreStop回调,您需要在更新函数时配置该回调函数。
登录函数计算控制台,在左侧导航栏,单击函数。
在顶部菜单栏,选择地域,然后在函数页面,单击目标函数。
在函数详情页,选择配置页签,在左侧导航栏,选择生命周期,然后单击编辑。
在编辑生命周期面板,设置回调程序和回调超时时间,然后单击部署。
说明每一个扩展函数都需要配置单独的回调程序和超时时间,其中回调程序格式为[文件名].[扩展函数名]。例如在Python Runtime中,创建函数时指定的PreStop回调为index.preStop,那么文件名为index.py,PreStop函数名为preStop。
配置回调函数后,您需要在代码执行中实现对应的函数。
单击代码页签,在代码编辑区域,输入扩展函数代码。
例如,您配置的PreStop回调程序为
index.preStop
,则需要实现preStop函数。不同语言运行时实现函数实例生命周期回调的方法请参见函数实例生命周期回调方法。说明在线IDE支持PHP、Python、Node.js和Custom Runtime;但不支持Java、Go和.NET这类编译性语言,以及Custom Container。
单击代码编辑器上方的部署代码,然后单击测试函数。
通过Serverless Devs配置回调
使用Serverless Devs配置PreStop扩展函数时,s.yaml
配置文件示例代码片段如下所示:
codeUri: './code.zip'
......
instanceLifecycleConfig:
preStop:
handler: index.PreStop
timeout: 60
如果您需要关闭某个扩展函数,需要将扩展函数的handler
参数显示置空,否则后端默认不更新。例如关闭PreStop函数,您需要按照以下配置进行部署更新,此时PreStop函数的timeout
参数已无效。
codeUri: './code.zip'
......
instanceLifecycleConfig:
preStop:
handler: ""
timeout: 60
不同语言运行时实现函数实例生命周期回调的方法请参见函数实例生命周期回调方法。
通过SDK配置回调
您可以通过SDK部署和更新扩展函数。本文介绍如何获取在创建函数时配置PreFreeze函数的SDK示例代码。
进入CreateFunction - 创建函数页面,单击调试,进入OpenAPI门户。
在参数配置页签,根据需要创建函数的基本信息填写输入参数。
参数配置完成后,单击SDK 示例页签,获取对应语言的SDK示例代码。
不同语言运行时实现函数实例生命周期回调的方法请参见函数实例生命周期回调方法。
函数实例生命周期回调方法
函数计算中所有运行时均支持Initialize和PreStop两种回调方法。运行时实现函数实例生命周期回调的方法请参考以下内容。
运行时 | 描述 | 参考文档 |
Node.js | 通过Node.js实现并应用函数实例生命周期回调方法。 | |
Python | 通过Python实现并应用函数实例生命周期回调方法。 | |
PHP | 通过PHP实现并应用函数实例生命周期回调方法。 | |
Java | 通过Java运行时实现函数实例生命周期回调的方法。 | |
C# | 通过C#运行时实现函数实例生命周期回调的方法。 | |
Go | 通过Go实现函数实例生命周期回调的方法。 | |
Custom Runtime | 通过Custom Runtime实现函数实例生命周期回调的方法。 | |
Custom Container | 通过Custom Container Runtime实现函数实例生命周期回调的方法。 |
查询回调函数相关日志
配置函数实例生命周期回调并执行代码实现对应的回调函数后,您可以查询实例生命周期回调函数的相关日志。
登录函数计算控制台,在左侧导航栏,单击函数。
在顶部菜单栏,选择地域,然后在函数页面,单击目标函数。
在函数详情页,选择日志页签,然后在调用请求页签,单击目标请求行右侧操作列的高级日志。
您可以使用复制的实例ID,查询所有生命周期回调函数的Start/End日志;还可以使用
实例ID+函数实例生命周期回调关键字
查询指定回调函数的Start/End日志,例如,c-62833f38-20f1629801fa4bd***** and PreStop
。此外,您还可以根据Start/End日志中的RequestId查询请求的日志信息。如果用户日志中没有RequestId,可以单击该日志中的图标获取上下文日志。