You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:09:53