函数实例生命周期回调

当您实现并配置函数实例生命周期回调后,函数计算将在相关实例生命周期事件发生时调用对应的回调程序。本文介绍自定义镜像实现函数实例生命周期回调的方法。

背景信息

函数实例生命周期涉及InitializerPreStop回调。Initializer回调包含调用代码执行指令两种类型,目前仅GPU函数支持执行指令类型Initializer回调。更多信息,请参见配置实例生命周期

函数实例生命周期回调程序与正常调用请求计费规则一致,但其执行日志只能在实时日志函数日志高级日志中查询,调用请求列表不会展示回调程序日志。具体操作,请参见查看实例生命周期回调函数日志

说明

目前执行指令类型回调产生的日志暂不支持写入函数日志。

回调方法实现

函数计算会在相关实例生命周期事件发生时调用对应的回调程序。函数实例生命周期涉及InitializerPreStop回调。Initializer回调程序包含调用代码执行指令两种类型,二者不允许同时配置,只能有一个生效。

调用代码

配置调用代码类型的回调程序后,函数实例启动或停止时,系统会向您的函数发送HTTP请求POST /initializeGET /pre-stop,您需要在业务代码中响应该请求。

Path

输入请求

期望的响应

(可选)POST /initialize

响应体:函数Initializer的返回值。

StatusCode

  • 2xx:成功状态。

  • 2xx:失败状态。

说明

Initializer回调程序执行超时或失败时,服务端始终返回HTTP 200状态码,必须通过响应头X-Fc-Error-Type:InitializationError或响应体中的errorMessage字段判断是否因初始化失败导致错误。

(可选)GET /pre-stop

响应体:函数PreStop的返回值。

StatusCode

  • 2xx:成功状态。

  • 2xx:失败状态。

如果您想在自定义镜像使用Initializer回调方法,您需要在您的HTTP Server中实现Path为/initializeMethodPOST的对应逻辑,使用PreStop回调方法同理。本文以自定义运行时Python 3.10为例,具体示例程序如下所示。

说明

创建的函数不设置Initializer时,无需实现/initialize。此时,即使HTTP Server实现了/initialize,代码中的/initialize逻辑也无法被调用执行,PreStop同理。

import os
from flask import Flask
from flask import request

app = Flask(__name__)

@app.route('/initialize', methods=['POST'])
def init_invoke():
    rid = request.headers.get('x-fc-request-id')
    print("FC Initialize Start RequestId: " + rid)
    # do your things
    print("FC Initialize End RequestId: " + rid)
    return "OK"

@app.route('/', defaults={'path': ''})
@app.route('/<path:path>', methods=['GET', 'POST', 'PUT', 'DELETE'])
def hello_world(path):
    rid = request.headers.get('x-fc-request-id')
    print("FC invoke Start RequestId: " + rid)
    # do your things
    print("FC invoke End RequestId: " + rid)
    return "Hello, World!", 200, [('Function-Name', os.getenv('FC_FUNCTION_NAME'))]


@app.route('/pre-stop', methods=['GET'])
def prestop_invoke():
    rid = request.headers.get('x-fc-request-id')
    print("FC PreStop Start RequestId: " + rid)
    # do your things
    print("FC PreStop End RequestId: " + rid)
    return "OK"


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=9000)

除了上面正确的代码程序,Python语言中还有函数执行报错的场景,/initialize示例代码如下。

@app.route('/initialize', methods=['POST'])
def init():
    raise Exception("hahaha")
    return "OK", 200, []
@app.route('/initialize', methods=['POST'])
def init():
    return "OK", 404, []

执行指令

如果您希望函数实例启动后,向函数发送HTTP请求来做一些初始化操作,可以通过配置回调指令来实现。支持用户自定义Shell实现方式,例如/bin/bash/bin/sh/bin/csh/bin/zsh等,需要确保函数运行时环境支持对应的Shell实现方式。

函数计算控制台目前预置了/bin/bash/bin/sh两种方式,本文以/bin/sh方式为例,具体示例程序如下。

#!/bin/sh

URL="http://localhost:7860"
REQUEST_PATH="sdapi/v1/txt2img"
JSON_DATA='{
	"prompt": "",
	"steps": 1,
	"height": 8,
	"width": 8
}'
temp_file=$(mktemp)
trap 'rm -f "$temp_file"' EXIT

if ! http_code=$(curl -s -w "%{http_code}" \
	-H "Content-Type: application/json" \
	-d "$JSON_DATA" \
	-o "$temp_file" \
	-X POST "$URL"/"$REQUEST_PATH"); then
	echo "{\"status\": \"curl_error\", \"code\": $curl_exit_code}" \
	| jq . >&2
	exit 1
fi

response=$(<"$temp_file")
echo "http code $http_code"

if [ "$http_code" -eq 200 ]; then
	echo "$response" | jq -r '.' || printf "%s\n" "$response"
	exit 0
else
	echo "$response_body" \
	| jq -c 'if type == "object" then . else {raw: .} end' 2>/dev/null \
	| jq -s '{status: "http_error", code: $code, response: .[0]}' \
		--arg code "$http_code" \
		>&2
	exit 1
fi

当回调指令退出码为0时,表示执行成功,否则表示执行失败。您可以在脚本中适当将一些错误信息输出至标准错误(stderr)中,方便定位错误原因,例如下面代码片段表示curl请求执行失败后,输出相关错误信息,并将退出码置为1。

if ! http_code=$(curl -s -w "%{http_code}" \
	-H "Content-Type: application/json" \
	-d "$JSON_DATA" \
	-o "$temp_file" \
	-X POST "$URL"/"$REQUEST_PATH"); then
	echo "{\"status\": \"curl_error\", \"code\": $curl_exit_code}" \
	| jq . >&2
	exit 1
fi

回调错误码

调用代码

错误码ID

解释说明

400

  • 函数Initializer回调失败返回400404,不会重新发送请求,但系统会继续重试直到调用成功为止。

  • 函数PreStop回调失败返回400404,不会影响函数实例的冷冻和停止。

404

500

函数计算重启实例。

执行指令

若指令退出码为非0,不会重新执行,系统会返回相关错误。

配置生命周期回调函数

  1. 登录函数计算控制台,在左侧导航栏,单击函数

  2. 在顶部菜单栏,选择地域,然后在函数页面,单击目标函数。

  3. 在函数详情页,选择配置页签,然后在实例配置区域,单击编辑

  4. 在实例配置面板,设置Initializer回调程序和回调超时时间。

    • 调用代码

      image

    • 执行指令

      控制台执行指令前置了/bin/bash/bin/sh两种Shell实现方式。

      image

  5. 在实例配置面板,继续设置PreStop回调程序和回调超时时间,然后单击部署

    image

查看实例生命周期回调函数日志

说明

目前执行指令类型回调产生的日志暂不支持写入函数日志。

您可以通过函数日志功能查看回调函数日志。

  1. 登录函数计算控制台,在左侧导航栏,单击函数

  2. 在顶部菜单栏,选择地域,然后在函数页面,单击目标函数。

  3. 在函数详情页面,选择测试页签,单击测试函数,然后选择日志 > 函数日志

    函数日志页签,您可以查看函数的调用日志和Initializer回调日志,示例如下。

    2024-06-26 10:59:23FC Initialize Start RequestId: 529eab23-9b3a-4ffc-88c8-9a686*******
    2024-06-26 10:59:23FC Initialize End RequestId: 529eab23-9b3a-4ffc-88c8-9a686*******
    2024-06-26 10:59:25FC Invoke Start RequestId: 1-667b840c-15c49df0-b7dc1*******
    2024-06-26 10:59:25FC Invoke End RequestId: 1-667b840c-15c49df0-b7dc1*******

    因为每个函数实例会缓存一段时间,不会马上销毁,因此不能立即查看PreStop回调日志。如需快速触发PreStop回调,可更新函数配置或者函数代码。更新完成后,再次查看函数日志,您可以查看PreStop回调日志。示例如下。

    2024-06-26 11:04:33FC PreStop Start RequestId: c4385899-f071-490e-a8b7-e33c5*******
    2024-06-26 11:04:33FC PreStop End RequestId: c4385899-f071-490e-a8b7-e33c5*******