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

NetSuite定时脚本调用API超时 求批量等待120秒优化方案

问题分析

当前触发的SSS_REQUEST_TIME_EXCEEDED错误,本质是短时间内发送过多API请求触发了对方的速率限制(API文档明确限制为每秒3-10次),且原代码未对请求频率做任何控制,当处理记录超过10条时就会超出限制阈值。结合API文档要求,我们需要实现主动控制请求间隔和429错误重试等待的逻辑。

解决方案

以下是修改后的代码,核心优化点包括:分批处理搜索结果、每批处理后添加延迟、捕获429错误并等待120秒重试、模块化拆分逻辑提高可维护性。

define(['N/search', 'N/record', 'N/https', 'N/runtime'], function(search, record, https, runtime) {

    // 可配置参数,根据API限制调整
    const BATCH_SIZE = 5; // 每批处理的记录数,保守匹配API速率限制
    const POST_BATCH_WAIT = 1000; // 每批处理完成后的等待时间(毫秒)
    const RATE_LIMIT_WAIT = 120000; // 遇到429错误时的等待时间(120秒)
    const MAX_RETRIES = 3; // 遇到速率限制后的最大重试次数

    function execute(context) {
        const savedSearchId = 2808;
        const savedSearch = search.load({ id: savedSearchId });
        const searchResultSet = savedSearch.run();
        let start = 0;
        let results;

        // 循环分批获取并处理搜索结果
        do {
            results = searchResultSet.getRange({ start: start, end: start + BATCH_SIZE });
            if (results.length === 0) break;

            // 处理当前批次的每条记录
            results.forEach(result => {
                const salesOrderId = result.getValue({ name: 'internalid' });
                const fieldValue = result.getValue({ name: 'custbody37' });

                // 调用API并处理重试逻辑
                const apiResponse = callApiWithRetry(fieldValue);
                if (!apiResponse) return;

                // 更新销售订单
                updateSalesOrder(salesOrderId, apiResponse.data.last_event);
            });

            // 批次处理完成后等待,避免请求过快
            log.debug('Batch processed', `Waiting ${POST_BATCH_WAIT/1000}s before next batch`);
            sleep(POST_BATCH_WAIT);

            start += BATCH_SIZE;
        } while (results.length === BATCH_SIZE);
    }

    // 带重试逻辑的API调用函数
    function callApiWithRetry(fieldValue) {
        const url = 'https://api.XXXXXX.com/v4/XXXX';
        const headers = {
            'Tracking-Api-Key': 'XXXXXXX',
            'Content-Type': 'application/json'
        };
        const body = JSON.stringify({ "awb_number": fieldValue });

        for (let retry = 0; retry < MAX_RETRIES; retry++) {
            try {
                const response = https.post({ url: url, headers: headers, body: body });

                // 处理429速率限制错误
                if (response.code === 429) {
                    log.warn('Rate limit triggered', `Retrying after ${RATE_LIMIT_WAIT/1000}s (attempt ${retry+1}/${MAX_RETRIES})`);
                    sleep(RATE_LIMIT_WAIT);
                    continue;
                }

                // 处理其他非成功状态码
                if (response.code < 200 || response.code >= 300) {
                    log.error('API request failed', `Status: ${response.code}, Body: ${response.body}`);
                    return null;
                }

                // 解析并返回响应
                return JSON.parse(response.body);
            } catch (e) {
                // 捕获请求超时错误,按速率限制逻辑重试
                if (e.name === 'SSS_REQUEST_TIME_EXCEEDED') {
                    log.warn('Request timed out', `Retrying after ${RATE_LIMIT_WAIT/1000}s (attempt ${retry+1}/${MAX_RETRIES})`);
                    sleep(RATE_LIMIT_WAIT);
                    continue;
                }
                log.error('API request error', e);
                return null;
            }
        }

        log.error('Max retries reached', 'Failed to get valid API response');
        return null;
    }

    // 销售订单更新函数
    function updateSalesOrder(salesOrderId, lastEventValue) {
        try {
            const salesOrder = record.load({
                type: record.Type.SALES_ORDER,
                id: salesOrderId
            });

            salesOrder.setValue({
                fieldId: 'custbody417',
                value: lastEventValue
            });

            salesOrder.save();
            log.debug('Sales Order updated', `ID: ${salesOrderId}`);
        } catch (e) {
            log.error('Failed to update Sales Order', `ID: ${salesOrderId}, Error: ${e}`);
        }
    }

    // NetSuite兼容的睡眠函数
    function sleep(milliseconds) {
        runtime.wait({ duration: milliseconds });
    }

    return {
        execute: execute
    };

});
关键修改说明
  • 分批处理:使用getRange()分批拉取搜索结果,避免一次性处理过多记录导致请求集中爆发
  • 请求频率控制:每批处理完成后添加固定延迟,主动降低请求速率,减少触发限制的概率
  • 错误重试机制:捕获429错误和请求超时错误,严格按照API要求等待120秒后重试,最多重试3次
  • 逻辑模块化:将API调用、订单更新拆分为独立函数,便于后续维护和调整
  • 参数可配置:将批次大小、等待时间等参数设为常量,可根据实际API限制灵活调整
注意事项
  • 根据API实际速率限制调整BATCH_SIZE和POST_BATCH_WAIT:例如API允许每秒10次请求,可将BATCH_SIZE设为10,POST_BATCH_WAIT设为1000毫秒
  • 定时脚本存在执行时间限制(通常为30分钟),若需处理超大量记录,建议结合脚本调度或任务链分多次执行
  • 确保已在依赖中添加N/runtime模块,否则sleep函数无法正常工作

内容的提问来源于stack exchange,提问作者penone

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 15:30:05