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

Azure存储队列触发函数执行延迟问题排查求助

Azure Functions队列触发延迟问题排查

使用场景

我正在Azure Function上搭建Cypress测试环境,将Cypress代码部署在运行Linux虚拟机的Azure Function中。Cypress依赖部分系统级组件,但这些组件默认不存在于Azure Function实例里。
所需Linux系统依赖如下:

libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6 xauth xvfb

为解决依赖问题,我采用预安装所有依赖的Docker镜像部署。

Azure订阅配置

使用Azure Function高级计划,实例规格为EP1。
Azure Function高级计划EP1实例配置

函数触发方式

采用Azure存储队列触发函数。

问题详情

我的存储队列触发函数代码如下:

const cypress = require("cypress");
const fs = require("fs");
const path = require("path");
const startTest = require("./e2eTest/index");
const axios = require("axios");

module.exports = async function (context, myQueueItem) {
  await axios
    .get(
      `https://staging-personal-vm.com/?msg=${myQueueItem}&ts=${new Date().toLocaleTimeString()}&finished=false&execTime=0`
    )
    .then(console.log)
    .catch(console.log);

  let startTime = Date.now();
  try {
    // 启动Cypress测试
    await startTest(myQueueItem);
  } catch (error) {}
  let endTime = Date.now();

  await axios
    .get(
      `https://staging-personal-vm.com/?msg=${myQueueItem}&ts=${new Date().toLocaleTimeString()}&finished=true&execTime=${(
        (endTime - startTime) /
        1000 /
        60
      ).toFixed(2)}`
    )
    .then(console.log)
    .catch(console.log);
};

函数接收队列消息后会启动处理,并通过个人Azure虚拟机记录日志:函数启动时发送一条finished=false的日志,处理完成时发送finished=true的日志。

日志记录如下:

Msg Time Stamp  Finished    Execution Time
2   2:35:44 PM  FALSE   0
1   2:35:47 PM  FALSE   0
3   2:36:05 PM  FALSE   0
4   2:36:05 PM  FALSE   0
5   2:36:52 PM  FALSE   0
6   2:36:56 PM  FALSE   0
7   2:37:22 PM  FALSE   0
8   2:37:43 PM  FALSE   0
9   2:38:56 PM  FALSE   0
10  2:39:13 PM  FALSE   0
11  2:39:54 PM  FALSE   0
12  2:39:58 PM  FALSE   0
13  2:40:33 PM  FALSE   0
14  2:41:40 PM  FALSE   0
15  2:41:48 PM  FALSE   0
16  2:41:49 PM  FALSE   0
17  2:42:10 PM  FALSE   0
18  2:42:29 PM  FALSE   0
19  2:42:30 PM  FALSE   0
20  2:42:41 PM  FALSE   0
9   3:38:41 PM  FALSE   0 => 消息9的函数执行启动时间比消息2晚了约1小时,所有消息是一次性推入队列的。

所有消息同时推入队列,但第一条消息启动后,第9条消息启动间隔近1小时。我设置了batchSize=1,原以为Azure会为每个函数启动新虚拟机,不会有延迟,请问为什么会出现这种情况?

我的host.json配置如下:

{
  "version": "2.0",
  "logging": {
    "applicationInsights": {
      "samplingSettings": {
        "isEnabled": true,
        "excludedTypes": "Request"
      }
    }
  },
  "extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[3.*, 4.0.0)"
  },
  "functionTimeout": "00:59:00",
  "extensions": {
    "queues": {
      "batchSize": 1
    }
  }
}

希望了解Azure消息处理机制以及延迟原因。


问题分析与解答

1. Azure Functions高级计划的扩容逻辑

高级计划的扩容并非为每条消息立即启动新实例,而是遵循渐进式扩容规则:

  • 初始仅启动1个实例,当该实例的并发执行达到阈值(默认单个实例最多处理16个队列触发的并发函数),才会触发扩容。
  • 扩容过程存在延迟,尤其自定义Docker镜像场景下,EP1实例资源有限,镜像拉取、容器启动的耗时会进一步拉长扩容周期。

2. 队列触发并发配置遗漏

你仅设置了batchSize=1,但未配置maxConcurrentCalls(默认值为16)。这意味着单个实例会同时处理16条消息,剩余消息需等待当前实例的函数执行完成,或等待新实例扩容完成,直接导致后续消息启动延迟。

3. 自定义Docker镜像的启动开销

预安装依赖的Docker镜像体积通常较大,EP1实例的CPU、内存资源有限,拉取镜像、启动容器的时间远长于官方基础镜像,扩容新实例时的启动耗时会直接转化为消息处理延迟。

4. 消息可见性超时的潜在影响

队列消息被实例接收后会进入不可见状态,默认超时30秒。若函数执行超时,消息会被重新放回队列,可能导致重复处理(日志中消息9出现两次启动日志可能与此相关)。


解决方案建议

  • 调整队列并发配置:在host.json的queues节点添加maxConcurrentCalls: 1,强制单个实例一次仅处理1条消息,触发更快扩容:
    "extensions": {
      "queues": {
        "batchSize": 1,
        "maxConcurrentCalls": 1
      }
    }
    
  • 优化Docker镜像:采用多阶段构建,清理不必要的依赖与缓存,减小镜像体积,加快拉取和启动速度。
  • 调整高级计划参数:在Azure门户设置计划的最小实例数,提前预热足够实例;同时调整扩容阈值,让Azure更早触发扩容。
  • 监控函数执行时间:确保Cypress测试的执行时间在functionTimeout范围内,避免因超时导致消息重新入队。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 17:40:30