本文介绍了使用函数计算编写代码过程中经常遇到的基本概念,包括函数入口、入口函数、函数入参、日志记录和错误处理。

函数入口

在创建函数时,需要指定函数入口,函数计算会从这个函数入口开始执行。函数入口的格式为[文件名].[函数名]

以 Node.js 为例,创建函数时指定的 Handlerindex.handler,那么函数计算会去加载 index.js 中定义的 handler 函数,并从这里开始执行。

入口函数

函数入口指定的函数叫做入口函数,类似于本地开发中的 main() 函数。入口函数需要满足函数计算提供的编程模型。

入口函数分为事件函数与 HTTP 函数:

  • 事件函数是普通的入口函数,是一段业务逻辑代码。除了 HTTP 函数的入口函数都是事件函数。
  • HTTP 函数是设置了 HTTP 触发器的函数,可以直接处理 HTTP Request 并返回 HTTP Response,适用于搭建 Web 应用。编程模型与事件函数不同。

以 Python 运行环境的事件函数为例:

def handler(event, context):
    return 'hello world'            

模型中指定固定的 event 事件数据(用户自定义)和 context 环境上下文(平台定义)作为函数入参。 在入口函数中,需对参数进行处理,并且可调用代码中定义的其他函数。 除了入口函数的定义外,代码的组织逻辑与本地函数相同。

各个编程语言的入口函数模型详情请参考对应的编程语言。

Initializer 函数

函数计算平台会根据请求动态分配实例执行函数,当函数有连续调用时,会复用同一实例。Initializer 是初始化函数,保证在同一实例中执行且成功执行一次。

您可以将数据库场景下连接池构建、函数依赖库加载等耗时长的业务逻辑放到 Initializer 函数中,避免每次运行函数都会做重复的操作,降低函数延时。

Initializer 函数在函数计算平台分配实例时执行,早于入口函数的执行。虽然 HTTP 函数与事件函数的编程模型不同,但他们对应的 Initializer 函数的编程模型都是相同的。

各个编程语言的 Initializer 函数模型详情请参考对应的编程语言。

函数入参

函数入参是指函数在调用时所传递给函数的内容。通常情况下,函数入参包括 event 入参和 context 入参两部分,但是由于开发语言和环境的不同,入参个数可能会有所不同。

  • event 参数

    event 参数是用户自定义的函数入参,以字节流的形式传给函数,数据结构由您自行定义,它可以是一个简单的字符串 、一个 JSON 对象、一张图片(二进制数据)。函数计算不对 event 参数的内容进行任何解释,您需要在函数中将字节流转换成相应的类型。

    对于不同的函数触发情况,event 参数的值会有以下区别:

    • 事件源服务触发函数时,事件源服务会将事件以一种平台预定义的格式作为 event 参数传给函数,您可以根据此格式编写代码并从 event 参数中获取信息。例如使用 OSS 触发器触发函数时会将 Bucket 及文件的具体信息以 JSON 格式传递给 event 参数。
    • 函数通过 SDK 直接调用时,您可以在调用方和函数代码之间自定义 event 参数。调用方按照定义好的格式传入数据,函数代码按格式获取数据。例如定义一个 JSON 类型的数据结构 {"key":"val"} 作为 event,当调用方传入数据 {"key":"val"} 时,函数代码先将字节流转换成 JSON,再通过 event["key"] 来获得值 val

    下文以 Python 为例,演示 event 参数的使用。

    import json
    def handler(event, context):
        evt = json.loads(event)
        print(evt['key'])
        return 'success'
    					
  • context 参数

    context 参数是函数计算平台定义的函数入参,它的数据结构由函数计算设计,包含函数运行时的信息,函数结构如下所示。

    • 函数结构
      {  
         requestId: '9cda63c3-1ac9-45ba-8a59-2593bb9bc101',  
         credentials: {    
            accessKeyId: 'xxx',    
            accessKeySecret: 'xxx',    
            securityToken: 'xxx'  
         },  
         function: {    
            name: 'xxx',    
            handler: 'index.handler',    
            memory: 512,    
            timeout: 60,    
            initializer: 'index.initializer',    
            initializationTimeout: 10  
         },  
         service: {    
            name: 'xxx',    
            logProject: 'xxx',    
            logStore: 'xxx',    
            qualifier: 'xxx',    
            versionId: 'xxx'  
         },  
         region: 'xxx',  
         accountId: 'xxx'
      }
    • 使用场景
      • 用户的临时密钥信息可以通过 context.credentials 获取,通过 context 中的临时密钥去访问阿里云的其他服务(使用示例中以访问 OSS 为例),避免了在代码中使用密钥硬编码。
      • context 中可以获取本次执行的基本信息,例如 requestId、serviceName、functionName、qualifier 等。
    • 使用示例

      下文以 Python 为例,演示使用 context 中的个人信息访问 OSS 的过程。

      import json
      import oss2
      def handler(event, context):    
          creds = context.credentials    
          auth = oss2.StsAuth(creds.access_key_id, creds.access_key_secret, creds.security_token)    
          bucket = oss2.Bucket(auth, 'oss-endpoint', 'your-bucket-name')    
          bucket.put_object('object-name', 'Awesome FC')    
          return 'success'

日志记录

函数计算与日志记录集成,函数计算会将函数调用的记录以及函数代码中打印的日志全部存储到日志库中,您可以使用函数计算提供的日志语句记录函数日志,方便系统调试及定位问题。

说明
  • 您需要在 Service 级别配置日志库,函数计算将函数日志发送到指定日志库中。
  • 通过控制台创建的函数、服务会为您自动创建并配置日志库。
开发语言 函数代码的打印日志语句 函数计算提供的日志语句 参考文档
Node.js console.log() console.log() 打印日志
Python print() logging.getLogger().info() 打印日志
Java System.out.println() context.getLogger().info() 打印日志
PHP echo "" . PHP_EOL $GLOBALS['fcLogger']->info() 打印日志
C# Console.WriteLine("") context.Logger.LogInformation() 打印日志

使用编程语言内嵌的打印输出语句打印的日志也会被收集到日志库里,而使用函数计算提供的日志语句打印的每条日志前都会带上请求 ID,方便日志的筛选。

#使用编程语言内嵌的打印输入语句打印的日志
# print('hello world')
message:  hello world

#使用函数计算提供的日志语句打印的日志
# logger.info('hello world')
message:  2020-03-13T04:06:49.099Z f84a9f4f-2dfb-41b0-9d6c-1682a2f3a650 [INFO] hello world
			

日志组成

函数运行日志包括服务名称、函数名称、当前执行版本、别名、代码日志。

函数运行日志数据结构如下所示。

__source__:  
__tag__:__receive_time__:  1584072413
__topic__:  myService
functionName:  myFunction
message:  2020-03-13T04:06:49.099Z f84a9f4f-2dfb-41b0-9d6c-1682a2f3a650 [INFO] hello world
qualifier:  LATEST
serviceName:  myService
versionId:         
  • 在函数开始执行时,系统会打印 FC Invoke Start RequestId: f84a9f4f-2dfb-41b0-9d6c-1682a2f3a650,标志函数执行开始。
  • 在函数执行完成时,系统会打印 FC Invoke End RequestId: f84a9f4f-2dfb-41b0-9d6c-1682a2f3a650,标志函数执行结束。

错误处理

函数计算的错误类型有两种,分别是 HandledInvocationErrorUnhandledInvocationError

  • HandledInvocationError

    只有在 Node.js 中通过 callback 返回的错误是 HandledInvocationError,错误信息在响应内容中。

    'use strict';
      module.exports.handler = function(event, context, callback) {
        console.log('hello world');
        callback('this is error', 'hello world');
      }          

    响应内容如下所示。

    {"errorMessage":"this is error"}          
  • UnhandledInvocationError

    除了 HandledInvocationError ,其余的错误都是 UnhandledInvocationError

    UnhandledInvocationError 的错误 stackTrace 会打印到日志中,您可以进入日志查看上下文,找到对应的 stackTrace