本文介紹Go實(shí)現(xiàn)函數(shù)實(shí)例生命周期回調(diào)的方法。

背景信息

當(dāng)您實(shí)現(xiàn)并配置函數(shù)實(shí)例生命周期回調(diào)后,函數(shù)計(jì)算系統(tǒng)將在相關(guān)實(shí)例生命周期事件發(fā)生時(shí)調(diào)用對(duì)應(yīng)的回調(diào)程序。函數(shù)實(shí)例生命周期涉及Initializer、PreFreeze和PreStop三種回調(diào)。更多信息,請(qǐng)參見函數(shù)實(shí)例生命周期回調(diào)

函數(shù)實(shí)例生命周期回調(diào)程序與正常調(diào)用請(qǐng)求計(jì)費(fèi)規(guī)則一致,但其執(zhí)行日志只能在函數(shù)日志實(shí)例日志高級(jí)日志中查詢,調(diào)用請(qǐng)求列表不會(huì)展示回調(diào)程序日志。具體操作,請(qǐng)參見查看實(shí)例生命周期回調(diào)函數(shù)日志

重要 如您需要使用PreFreeze和PreStop回調(diào),請(qǐng)將fc-runtime-go-sdk升級(jí)到v0.1.0及以上版本。

回調(diào)方法簽名

  • 初始化回調(diào)程序(Initializer回調(diào))是在函數(shù)實(shí)例啟動(dòng)成功之后,運(yùn)行請(qǐng)求處理程序(Handler)之前執(zhí)行。函數(shù)計(jì)算保證在一個(gè)實(shí)例生命周期內(nèi),成功且只成功執(zhí)行一次Initializer回調(diào)。例如您的Initializer回調(diào)第一次執(zhí)行失敗了,系統(tǒng)會(huì)重試,直到成功為止,然后再執(zhí)行您的請(qǐng)求處理程序。因此,您在實(shí)現(xiàn)Initializer回調(diào)時(shí),需要保證它被重復(fù)調(diào)用時(shí)的正確性。
  • 預(yù)凍結(jié)回調(diào)程序(PreFreeze回調(diào))在函數(shù)實(shí)例凍結(jié)前執(zhí)行。
  • 預(yù)停止回調(diào)程序(PreStop回調(diào))在函數(shù)實(shí)例銷毀前執(zhí)行。
Initializer回調(diào)、PreFreeze回調(diào)和PreStop回調(diào)方法簽名相同,均只有一個(gè)Context輸入?yún)?shù),沒有返回參數(shù)。定義如下:
function(ctx context.Context)

回調(diào)方法實(shí)現(xiàn)

在代碼中實(shí)現(xiàn)生命周期回調(diào)方法,并使用相應(yīng)函數(shù)進(jìn)行注冊(cè)。三種回調(diào)方法的注冊(cè)方法如下:
// 注冊(cè)Initializer回調(diào)方法
fc.RegisterInitializerFunction(initialize)
// 注冊(cè)PreStop回調(diào)方法
fc.RegisterPreStopFunction(preStop)
// 注冊(cè)PreFreeze回調(diào)方法
fc.RegisterPreFreezeFunction(preFreeze)
示例程序如下所示。
package main

import (
    "context"
    "log"

    "github.com/aliyun/fc-runtime-go-sdk/fc"
    "github.com/aliyun/fc-runtime-go-sdk/fccontext"
)

func HandleRequest(ctx context.Context) (string, error) {
    return "hello world!", nil
}

func preStop(ctx context.Context) {
    log.Print("this is preStop handler")
    fctx, _ := fccontext.FromContext(ctx)
    fctx.GetLogger().Infof("context: %#v\n", fctx)
}

func preFreeze(ctx context.Context) {
    log.Print("this is preFreeze handler")
    fctx, _ := fccontext.FromContext(ctx)
    fctx.GetLogger().Infof("context: %#v\n", fctx)
}

func initialize(ctx context.Context) {
    log.Print("this is initialize handler")
    fctx, _ := fccontext.FromContext(ctx)
    fctx.GetLogger().Infof("context: %#v\n", fctx)
}

func main() {
    fc.RegisterInitializerFunction(initialize)
    fc.RegisterPreStopFunction(preStop)
    fc.RegisterPreFreezeFunction(preFreeze)
    fc.Start(HandleRequest)
}                
示例解析如下:
  • func initialize(ctx context.Context):Initializer回調(diào)方法。其中ctx context.Context參數(shù)提供了函數(shù)執(zhí)行時(shí)的上下文信息。更多信息,請(qǐng)參見上下文
  • func preStop(ctx context.Context):PreStop回調(diào)方法。其中ctx context.Context參數(shù)提供了函數(shù)執(zhí)行時(shí)的上下文信息。更多信息,請(qǐng)參見上下文
  • func preFreeze(ctx context.Context):PreFreeze回調(diào)方法。其中ctx context.Context參數(shù)提供了函數(shù)執(zhí)行時(shí)的上下文信息。更多信息,請(qǐng)參見上下文
  • func main():運(yùn)行FC函數(shù)代碼的入口點(diǎn),Go程序必須包含main函數(shù)。通過添加代碼fc.Start(HandleRequest),設(shè)置請(qǐng)求處理程序的執(zhí)行方法;通過添加代碼 fc.RegisterInitializerFunction(initialize),注冊(cè)Initializer回調(diào)方法。同理,注冊(cè)PreStop和PreFreeze回調(diào)方法。
    重要
    • 上述示例僅適用于事件請(qǐng)求處理程序(Event Handler)。如果是HTTP請(qǐng)求處理程序(HTTP Handler),您需要將示例中的fc.Start(HandleRequest)換成fc.StartHttp(HandleRequest)
    • 注冊(cè)生命周期回調(diào)方法必須在fc.Start(HandleRequest)fc.StartHttp(HandleRequest)之前執(zhí)行,否則會(huì)導(dǎo)致注冊(cè)失敗。

配置生命周期回調(diào)函數(shù)

通過控制臺(tái)配置

函數(shù)計(jì)算控制臺(tái)FC函數(shù)配置中,啟用Initializer回調(diào)程序PreFreeze回調(diào)程序PreStop回調(diào)程序。示例如下。

db-go-lifecycle

具體步驟,請(qǐng)參見函數(shù)實(shí)例生命周期

通過Serverless Devs配置

使用Serverless Devs配置

如果使用Serverless Devs工具,需要在s.yaml配置文件中添加Initializer 回調(diào)程序PreFreeze 回調(diào)程序PreStop 回調(diào)程序
  • Initializer回調(diào)配置

    function配置下添加initializerinitializationTimeout兩個(gè)字段。

  • PreFreeze回調(diào)配置

    function配置下添加instanceLifecycleConfig.preFreeze字段,包括handlertimeout兩個(gè)字段。

  • PreStop回調(diào)配置

    function配置下添加instanceLifecycleConfig.preStop字段,包括handlertimeout兩個(gè)字段。

重要 handler字段需配置為非空字符串,在s.yaml文件中使用默認(rèn)值"true"時(shí),必須攜帶雙引號(hào)。

具體的示例如下所示。

edition: 1.0.0
name: hello-world  #  項(xiàng)目名稱
access: default    #  密鑰別名

vars: # 全局變量
  region: cn-shanghai # 地域
  service:
    name: fc-example
    description: 'fc example by serverless devs'

services:
  helloworld: # 業(yè)務(wù)名稱/模塊名稱
    component: fc
    props: #  組件的屬性值
      region: ${vars.region}
      service: ${vars.service}
      function:
        name: golang-lifecycle-hook-demo
        description: 'fc example by serverless devs'
        runtime: go1
        codeUri: ./target
        handler: main
        memorySize: 128
        timeout: 60
        initializationTimeout: 60
        initializer: "true"
        instanceLifecycleConfig:
          preFreeze:
            handler: "true"
            timeout: 30
          preStop:
            handler: "true"
            timeout: 30

關(guān)于Serverless Devs的YAML配置規(guī)范,請(qǐng)參見Serverless Devs操作命令

查看實(shí)例生命周期回調(diào)函數(shù)日志

您可以通過函數(shù)日志功能查看回調(diào)函數(shù)日志。

  1. 登錄函數(shù)計(jì)算控制臺(tái),在左側(cè)導(dǎo)航欄,單擊服務(wù)及函數(shù)
  2. 在頂部菜單欄,選擇地域,然后在服務(wù)列表頁(yè)面,單擊目標(biāo)服務(wù)。
  3. 函數(shù)管理頁(yè)面,單擊目標(biāo)函數(shù)名稱,然后在函數(shù)詳情頁(yè)面,單擊測(cè)試函數(shù)頁(yè)簽。
  4. 測(cè)試函數(shù)頁(yè)簽,單擊測(cè)試函數(shù),然后選擇調(diào)用日志 > 函數(shù)日志
    函數(shù)日志頁(yè)簽,您可以查看函數(shù)的調(diào)用日志、Initializer回調(diào)日志和PreFreeze回調(diào)日志,示例如下。
    2022-10-09 19:26:17 FunctionCompute dotnetcore3.1 runtime inited.
    2022-10-09 19:26:17 FC Initialize Start RequestId: 793ad2f1-9826-4d9a-90d9-5bf39e******
    2022-10-09 19:26:17 2022-10-09 19:26:17 793ad2f1-9826-4d9a-90d9-5bf39e****** [INFO] Initialize start
    2022-10-09 19:26:17 2022-10-09 19:26:17 793ad2f1-9826-4d9a-90d9-5bf39e****** [INFO] Handle initializer: 793ad2f1-9826-4d9a-90d9-5bf39e******
    2022-10-09 19:26:17 2022-10-09 19:26:17 793ad2f1-9826-4d9a-90d9-5bf39e****** [INFO] Initialize end
    2022-10-09 19:26:17 FC Initialize End RequestId: 793ad2f1-9826-4d9a-90d9-5bf39e******
    2022-10-09 19:26:17 FC Invoke Start RequestId: 793ad2f1-9826-4d9a-90d9-5bf39e******
    2022-10-09 19:26:17 2022-10-09 19:26:17 793ad2f1-9826-4d9a-90d9-5bf39e****** [INFO] Handle request: 793ad2f1-9826-4d9a-90d9-5bf39e******
    2022-10-09 19:26:17 FC Invoke End RequestId: 793ad2f1-9826-4d9a-90d9-5bf39e******
    2022-10-09 19:26:17 FC PreFreeze Start RequestId: 793ad2f1-9826-4d9a-90d9-5bf39e******
    2022-10-09 19:26:17 2022-10-09 19:26:17 793ad2f1-9826-4d9a-90d9-5bf39e****** [INFO] PreFreeze start
    2022-10-09 19:26:17 2022-10-09 19:26:17 793ad2f1-9826-4d9a-90d9-5bf39e****** [INFO] Handle PreFreeze: 793ad2f1-9826-4d9a-90d9-5bf39e******
    2022-10-09 19:26:17 2022-10-09 19:26:17 793ad2f1-9826-4d9a-90d9-5bf39e****** [INFO] PreFreeze end
    2022-10-09 19:26:17 FC PreFreeze End RequestId: 793ad2f1-9826-4d9a-90d9-5bf39e******
    因?yàn)槊總€(gè)函數(shù)實(shí)例會(huì)緩存一段時(shí)間,不會(huì)馬上銷毀,因此不能立即查看PreStop回調(diào)日志。如需快速觸發(fā)PreStop回調(diào),可更新函數(shù)配置或者函數(shù)代碼。更新完成后,再次查看函數(shù)日志,您可以查看PreStop回調(diào)日志。示例如下。
    2022-10-09 19:32:17 FC PreStop Start RequestId: 03be685c-378b-4736-8b08-a67c1d*****
    2022-10-09 19:32:17 2022-10-09 19:32:17 03be685c-378b-4736-8b08-a67c1d***** [INFO] PreStop start
    2022-10-09 19:32:17 2022-10-09 19:32:17 03be685c-378b-4736-8b08-a67c1d***** [INFO] Handle PreStop: 03be685c-378b-4736-8b08-a67c1d*****
    2022-10-09 19:32:17 2022-10-09 19:32:17 03be685c-378b-4736-8b08-a67c1d***** [INFO] PreStop end
    2022-10-09 19:32:17 FC PreStop End RequestId: 03be685c-378b-4736-8b08-a67c1d*****

示例程序

函數(shù)計(jì)算為您提供了Initializer回調(diào)的示例程序。該示例為您展示了如何使用Go運(yùn)行時(shí)的Initializer回調(diào)來(lái)初始化MySQL連接池。在本示例中,MySQL數(shù)據(jù)庫(kù)配置在函數(shù)的環(huán)境變量配置中(參考s.yaml)。Initializer回調(diào)從環(huán)境變量中獲取數(shù)據(jù)庫(kù)配置,創(chuàng)建MySQL連接池并測(cè)試連通性。

更多信息,請(qǐng)參見go-initializer-mysql