基于AWS Lambda的Serverless定时任务重复执行问题求助
解决AWS Lambda定时任务重复执行的问题
这种重复触发的情况在AWS Schedule Event + Lambda的场景里确实挺常见,大概率是AWS的调度重试或者并发执行导致的。下面给你几个靠谱的解决思路,从根本上确保任务只执行一次:
1. 核心方案:实现幂等性(最可靠)
不管Lambda被触发多少次,我们的代码要能判断「这个任务已经执行过了」,直接跳过重复执行。最常用的方式是用一个持久化存储(比如DynamoDB)记录任务的执行状态,每次执行前先校验。
改造代码示例
第一步:更新handler.js,加入幂等校验逻辑
const axios = require("axios"); const { lessonsOfHN } = require("./src/lessonsOfHN"); const AWS = require("aws-sdk"); const dynamodb = new AWS.DynamoDB.DocumentClient(); // 生成唯一任务ID:因为是每周日执行一次,用当天UTC日期作为唯一标识就足够 const getUniqueTaskId = () => { const date = new Date(); return `${date.getUTCFullYear()}-${String(date.getUTCMonth()+1).padStart(2, '0')}-${String(date.getUTCDate()).padStart(2, '0')}`; }; exports.run = async (event, context, callback) => { const taskId = getUniqueTaskId(); const time = new Date(); console.log(`Cron function "${context.functionName}" triggered at ${time}`); // 先检查今日任务是否已执行 try { const existingTask = await dynamodb.get({ TableName: process.env.DYNAMODB_TABLE, Key: { taskId } }).promise(); if (existingTask.Item) { console.log("今日任务已执行,跳过重复触发"); return callback(null, { lesson: null, success: true, message: "Skipped duplicate execution" }); } } catch (err) { // 只有当表不存在或者查询出错时才抛出异常,没查到数据属于正常情况 if (err.code !== 'ResourceNotFoundException') { return callback(err); } } // 执行实际推送逻辑 const lesson = await lessonsOfHN(); const ENDPOINT = `https://api.telegram.org/bot${ process.env.BOT_API_KEY }/sendMessage`; await axios({ method: "get", url: ENDPOINT, data: { chat_id: process.env.CHANNEL_ID, parse_mode: "markdown", disable_web_page_preview: true, text: lesson } }); // 标记任务已执行,写入DynamoDB await dynamodb.put({ TableName: process.env.DYNAMODB_TABLE, Item: { taskId, executedAt: time.toISOString() } }).promise(); callback(null, { lesson, success: true }); };
第二步:更新serverless.yml,添加DynamoDB资源和权限
service: lessons-of-hn-telegram-bot provider: name: aws runtime: nodejs8.10 environment: BOT_API_KEY: ${file(./env.json):BOT_API_KEY} CHANNEL_ID: ${file(./env.json):CHANNEL_ID} DYNAMODB_TABLE: CronTaskExecutions # 给Lambda添加操作DynamoDB的权限 iamRoleStatements: - Effect: Allow Action: - dynamodb:GetItem - dynamodb:PutItem Resource: "arn:aws:dynamodb:${self:provider.region}:*:table/${self:provider.environment.DYNAMODB_TABLE}" functions: cron: handler: handler.run description: Cron job that runs every Sunday at 10 am UTC timeout: 10 # 合理设置超时时间,避免因超时触发AWS重试 events: - schedule: cron(0 10 ? * SUN *) # 定义DynamoDB表资源 resources: Resources: CronTaskExecutionsTable: Type: AWS::DynamoDB::Table Properties: TableName: ${self:provider.environment.DYNAMODB_TABLE} AttributeDefinitions: - AttributeName: taskId AttributeType: S KeySchema: - AttributeName: taskId KeyType: HASH ProvisionedThroughput: ReadCapacityUnits: 1 WriteCapacityUnits: 1
2. 辅助方案:限制Lambda并发
把这个定时任务的Lambda函数预留并发设为1,这样同一时间只能有一个实例运行,避免并行重复执行。不过这个只能解决并发触发的情况,无法处理延迟的重试(比如第一次执行超时后的重试),建议和幂等性方案配合使用。
3. 排查超时与重试配置
检查Lambda的超时时间是否过短,如果任务执行接近超时,AWS可能会触发重试。你可以在serverless.yml的函数配置里延长超时时间(比如上面示例里的timeout: 10)。另外,AWS Schedule Event默认有重试机制,幂等性设计是应对这种情况的最佳方式。
内容的提问来源于stack exchange,提问作者deadcoder0904
相关产品推荐
相关产品推荐

