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

Azure函数本地调试正常,部署至Azure后间歇性超时

问题排查:Azure函数写入PostgreSQL超时及连接终止错误

背景

参考Azure PostgreSQL IoT Demo示例搭建数据流转流程:ESP32S3采集数据 → Azure IoT Hub → Azure Function Application → Azure PostgreSQL。

原始函数代码

module.exports = function (context, IoTHubMessages) {
    var pg = require('pg');
    
    const config = {
                        host: 'myserver.postgres.database.azure.com',
                        user: 'granted', 
                        password: 'mypassword',
                        database: 'telemetry',
                        port: 5432,
                        ssl: true
    };
    
    var client = new pg.Client(config);
    const query = 'insert into tempdata(deviceid, data) values(1,\'' + JSON.stringify(IoTHubMessages) + '\');';
    context.log(query);

    client.connect(err => {
        if (err) throw err;
        else {
            client
                .query(query)
                .then(() => {
                context.log('insert completed successfully!');
                client.end(console.log('Closed client connection'));
                 })
            .catch(err => console.log(err))
            .then(() => {
                context.log('Finished execution, exiting now');
                        });
            }
        });
        
};

部署后问题

VSCode本地调试每次完美运行,但部署至Azure后,函数正常执行一次,随后触发5分钟超时,之后又正常执行一次,再次超时。Azure日志如下:

2023-08-08T21:25:06Z [Information] insert into tempdata(deviceid,
data) values(1,'["{ "Temperature": 82.22}","{ "Temperature":
82.22}","{ "Temperature": 82.22}","{ "Temperature": 82.22}","{ "Temperature": 82.22}","{ "Temperature": 82.22}","{
"Temperature": 82.22}","{ "Temperature": 82.22}","{
"Temperature": 82.22}","{ "Temperature": 82.22}"]');
2023-08-08T21:25:06Z [Information] insert completed successfully!
2023-08-08T21:25:06Z [Information] Finished execution, exiting now
2023-08-08T21:30:06Z [Error] Timeout value of 00:05:00 exceeded by
function 'Functions.IoTHub_EventHub3' (Id:
'c7fddcd1-3953-4562-be81-0f04a8f29b48'). Initiating cancellation.
2023-08-08T21:30:06Z [Error] Executed 'Functions.IoTHub_EventHub3'
(Failed, Id=c7fddcd1-3953-4562-be81-0f04a8f29b48, Duration=300017ms)
2023-08-08T21:30:18Z [Information] Executing
'Functions.IoTHub_EventHub3' (Reason='(null)',
Id=70402ee6-e981-44ef-950c-2c7ced54156b)
2023-08-08T21:30:18Z [Information] Trigger Details: PartionId: 0, Offset:
4295065936-4295069176, EnqueueTimeUtc:
2023-08-08T20:32:44.3110000+00:00-2023-08-08T20:34:14.7240000+00:00,
SequenceNumber: 1831-1840, Count: 10
2023-08-08T21:30:18Z [Verbose]
Sending invocation id: '70402ee6-e981-44ef-950c-2c7ced54156b
2023-08-08T21:30:18Z [Verbose] Posting invocation
id:70402ee6-e981-44ef-950c-2c7ced54156b on
workerId:8ea99dea-4f2a-4325-854e-eb18ae1d2ab7
2023-08-08T21:30:18Z [Information] insert into tempdata(deviceid, data) values(1,'["{
"Temperature": 82.22}","{ "Temperature": 82.22}","{
"Temperature": 82.22}","{ "Temperature": 82.22}","{
"Temperature": 82.22}","{ "Temperature": 82.22}","{
"Temperature": 82.22}","{ "Temperature": 82.40}","{
"Temperature": 82.22}","{ "Temperature": 82.22}"]');
2023-08-08T21:30:19Z [Information] insert completed successfully!
2023-08-08T21:30:19Z [Information] Finished execution, exiting now
2023-08-08T21:35:18Z [Error] Timeout value of 00:05:00 exceeded by
function 'Functions.IoTHub_EventHub3' (Id:
'70402ee6-e981-44ef-950c-2c7ced54156b'). Initiating cancellation.

修改异步函数后的问题

尝试将代码改为异步函数后,本地调试出现连接终止错误:

module.exports = async function (context, IoTHubMessages) {
    var pg = require('pg');
    
    const config = {
                        host: 'arduinodata.postgres.database.azure.com',
                        user: 'granted', 
                        password: 'mypassword',
                        database: 'mydatabase',
                        port: 5432,
                        ssl: true
    };
    
    var client = new pg.Client(config);
    const query = 'insert into tempdata(deviceid, data) values(1,\'' + JSON.stringify(IoTHubMessages) + '\');';
    context.log(query);
    client.connect();
    client.query(query);
    

    client.end();
    await client.end();
    context.log('insert completed successfully!');  

/*
    client.connect(err => {
        if (err) throw err;
        else {
            client
                .query(query)
                .then(() => {
                context.log('insert completed successfully!');
                client.end(console.log('Closed client connection'));
                 })
            .catch(err => console.log(err))
            .then(() => {
                context.log('Finished execution, exiting now');
                        });
            }
        });*/
        
};

本地错误日志:

[error] Worker uncaught exception: Error: Connection terminated
at C:\Users\grant\Documents\GitHub\Arduino\AppFunc\node_modules\pg\lib\client.js:526:17
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

问题分析与解决方案

1. 原始代码超时原因

Azure Functions的Node.js运行时中,普通回调函数需要手动调用context.done()通知执行完成。原始代码完成数据库插入后未调用该方法,导致运行时认为函数仍在执行,直到5分钟超时才终止。本地调试环境未严格检查该逻辑,但Azure环境会强制执行超时规则。

2. 修改异步函数后的错误原因

异步函数版本存在多个问题:

  • client.connect()、client.query()均为异步操作,未等待完成就调用client.end(),导致连接被提前终止,触发"Connection terminated"错误。
  • 连续两次调用client.end(),第一次同步调用直接关闭连接,第二次await已无意义。

修复后的代码

使用async/await正确处理异步操作,确保函数正常结束:

module.exports = async function (context, IoTHubMessages) {
    const pg = require('pg');
    
    const config = {
        host: 'myserver.postgres.database.azure.com',
        user: 'granted', 
        password: 'mypassword',
        database: 'telemetry',
        port: 5432,
        ssl: true
    };
    
    const client = new pg.Client(config);
    try {
        // 等待连接建立
        await client.connect();
        // 使用参数化查询避免SQL注入,安全处理JSON数据
        const query = 'INSERT INTO tempdata(deviceid, data) VALUES($1, $2);';
        const values = [1, JSON.stringify(IoTHubMessages)];
        context.log(`执行查询: ${query} 参数: ${JSON.stringify(values)}`);
        // 等待查询执行完成
        await client.query(query, values);
        context.log('insert completed successfully!');
    } catch (err) {
        context.error('数据库操作失败:', err);
        throw err; // 抛出错误让Functions运行时处理
    } finally {
        // 确保连接关闭
        await client.end();
        context.log('Closed client connection');
    }
    // 异步函数无需调用context.done(),返回即可结束
};

额外建议

  • 参数化查询:原始代码直接拼接JSON字符串到SQL语句,存在SQL注入风险,修复后使用$1、$2参数化方式更安全。
  • 错误处理:添加try/catch捕获数据库操作错误,通过context.error记录日志,便于排查问题。
  • 连接池优化:若函数调用频繁,建议使用pg的连接池(pg.Pool)代替每次创建新客户端,提升性能并避免连接泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 18:54:54