编写第一个Lambda函数:Hello World

好,到了真正动手的时候了。这一章我们写第一个Lambda函数,说白了就是个"Hello World"。但别小看它——事件对象、上下文、日志输出这些核心概念,都会在这一小段代码里串起来。写完这个,你对Lambda的理解就不会只是"哦,一个云函数"了。

创建函数的两种姿势

我个人习惯在AWS控制台里先跑通,再用CLI或IaC去固化。初学者直接上控制台,路径是:

  • 进入Lambda服务首页
  • 点「创建函数」
  • 选「从头开始创作」
  • 函数名称:HelloWorldDemo
  • 运行时:Python 3.12(或你环境里最新的3.x版本)
  • 架构:x86_64就够,ARM便宜但第一次用没必要折腾
  • 权限:保持默认「创建基础Lambda执行角色」

点完创建,你就能看到内置代码编辑器了。这时候别急着写代码——先看看默认模板长什么样。

import json

def lambda_handler(event, context):
    # TODO implement
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

嗯,这就是Lambda函数的骨架。重点在函数签名:event和context两个参数,缺一不可。

事件对象:Lambda的输入信号

event说白了就是触发你的那个"事"。它可以是API Gateway传过来的HTTP请求,可以是S3新文件的通知,可以是CloudWatch定时器。具体长啥样?触发源说了算。

你想想看,如果是一个定时巡检任务,那event里可能只带一个时间戳。但如果是来自API Gateway,那body、headers、queryStringParameters全都有。

我在项目中遇到过最奇葩的event——某个第三方服务把XML当JSON传过来了,解析直接炸了。所以第一条经验:永远不要假设event结构。打印出来看才最稳。

调试小技巧: 创建测试事件时,点「测试」按钮旁边的小三角,选「配置测试事件」。可以先选一个模板(比如api-gateway-aws-proxy),然后自己改。测试完了别删,后面调试还能复用。

上下文对象:函数的运行环境快照

context里装的不是业务数据,而是"我是谁、还剩多少时间、日志组是哪个"这类运行时信息。我刚开始用Lambda时压根不看它,直到有一次函数超时了,才发现context里早就警告了剩余时间。

属性/方法 类型 说明 我的常用场景
function_name 字符串 当前函数名 日志里标记来源
function_version 字符串 函数版本($LATEST或具体数字) 版本回滚时排查
invoked_function_arn 字符串 完整ARN 跨账号调用时校验身份
memory_limit_in_mb 整数 分配的内存大小 性能分析时看是不是内存太小
aws_request_id 字符串 该次调用的唯一ID 关联日志和异常追踪
log_group_name 字符串 CloudWatch日志组名 直接跳转到对应日志组
log_stream_name 字符串 日志流名 并发高时区分不同实例
get_remaining_time_in_millis() 方法 返回剩余毫秒数 超时前做清理或回退

注意那个get_remaining_time_in_millis(),我在一个数据处理任务里用过它——如果剩余时间不足10秒,就保存当前进度并提前退出,避免数据写到一半被粗暴杀掉。这个技巧在定时巡检里很实用,因为巡检任务经常要扫大量资源,一下跑不完。

日志输出:CloudWatch是你唯一的朋友

在Lambda里不能用print大法慢慢看终端,因为根本没有终端。所有标准输出(stdout和stderr)会自动写入CloudWatch Logs。所以你写print(...)其实也能用,但官方推荐用logging模块。

import json
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):
    logger.info("函数开始执行")
    logger.info(f"事件内容: {json.dumps(event)}")
    logger.info(f"剩余时间(ms): {context.get_remaining_time_in_millis()}")
    
    # 你的业务逻辑放这里
    name = event.get('name', 'World')
    message = f"Hello, {name}!"
    
    logger.info(f"返回消息: {message}")
    return {
        'statusCode': 200,
        'body': json.dumps({'message': message})
    }
避坑指南: 我曾经在生产环境里把logging.setLevel(logging.DEBUG)忘了改成INFO,结果一个函数一天写了10GB日志,CloudWatch账单直接飙上去。建议生产环境至少用INFO级别,DEBUG只在开发测试时开。另外,《定时巡检》场景下日志量会随着巡检频率线性增长,一定要注意控制。

完整的Hello World(带参数版)

下面这个版本集成了一开始提到的三个核心点。你可以直接复制到编辑器中,然后创建一个测试事件:

{
  "name": "行云"
}

点「测试」——控制台会立即返回结果,同时CloudWatch里会多一条日志流。去CloudWatch控制台找到对应的日志组(名字就是/aws/lambda/HelloWorldDemo),点开最新日志流,你就能看到我们写的INFO日志了。

本章核心知识图

下面的SVG把函数调用时的数据流画出来了,看一眼能帮你理清事件、上下文、日志之间的协作关系。

Lambda函数调用数据流 触发源(定时器/API等) event 对象(JSON) context 对象(运行时) lambda_handler event + context → 业务逻辑 返回结果(JSON) CloudWatch Logs 关键:event决定“做什么”,context提供“环境实况”,日志记录“全过程” 三者配合,你才能在生产环境中定位任何一次调用的问题

最佳实践清单

  • 日志分级:线上用INFO,故障排查时临时开DEBUG,完事改回去
  • 打印event:每次新对接触发源,第一件事就是logger.info(event),看清结构再写解析代码
  • 关注剩余时间:长任务里定期检查context.get_remaining_time_in_millis(),别让Lambda死在半路
  • 测试事件别删:至少保留一个测试case,后面重构时一键验证
  • 别在代码里拼日志组名:直接用context.log_group_name,万一运维改了命名规则呢?
一句话总结: Lambda函数 = event(输入) + context(环境) + 你的逻辑 + 日志(输出)。三者配合,你才能像Debug本地程序一样自信地调试云函数。

写到这里你已经有一只脚踏进Lambda的大门了。接下来我们需要给它装上定时器——CloudWatch Events。那才是定时巡检自动化的真正起点。