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

函数入口

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

以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() Java 运行环境
PHP echo "" . PHP_EOL $GLOBALS['fcLogger']->info() PHP 运行环境
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