javascript - Azure Function 执行速度极慢且不一致

标签 javascript azure azure-sql-database azure-functions

我正在编写一些 Azure Functions 脚本来读取/写入内部数据库并将相关信息显示到网页中。

在加载调用 Azure Function 脚本的网页时,我注意到 Web UI 极其缓慢甚至超时。经过进一步调查,我意识到以下几点:

  • Azure Function 脚本有时需要 10 秒到 1 分钟以上的时间才能连接到 SQL 数据库。
  • 有时脚本会在几毫秒内运行,有时需要 3 分钟以上才能完全运行脚本。

  • 这是我的 Azure 函数脚本:
    module.exports = function(context, req) {
    
    context.log("Function Started: " + new Date());
    
    // Import package
    const sql = require('mssql');
    var _ = require('underscore-node');
    var moment = require('moment');
    var Promise = require('promise');
    // Create a configuration object for our Azure SQL connection parameters
    var config = {
        server: "***", // Use your SQL server name
        database: "***", // Database to connect to
        user: "***", // Use your username
        password: "***", // Use your password
        port: ***,
        // Since we're on Windows Azure, we need to set the following options
        options: {
            encrypt: true
        },
        multipleStatements: true,
        parseJSON: true
    };
    var flagDefinitionId = null;
    
    if (req.query.Id == null || req.query.Id == "" || req.query.Id.length == 0) {
        context.res = {
            // status: 200, /* Defaults to 200 */
            body: "No have flagDefinitionId "
        };
        context.done();
        // return;
    }
    
    var listTicketsFlag = [];
    
    flagDefinitionId = req.query.Id;
    sql.close();
    var DBSchema = "b8akjsms2_st.";
    sql.connect(config).then(function() {
        context.log("SQL Connected: " + new Date());
    
        var getAllEventTicketGoToMarket = new Promise(function(resolve, reject) {
            var queryGetEvent = ";WITH EventLog1 AS(" +
                " SELECT MD1, max([DateTime]) as LTime from " + DBSchema + "EventLog" +
                " where ([Event] = 'Ticket_Go_To_Market' OR [Event] = 'Acknowledge_Timeout')" +
                " group by MD1 )" +
                " SELECT * from ( SELECT EV.MD1 , EV.MD2," +
                " (SELECT COUNT(*) from " + DBSchema + "EventLog where MD1 = EV.MD1 and [Event] = 'Market_Ticket_Clear') as TotalClear" +
                " FROM " + DBSchema + "[Ticket] T" +
                " JOIN (SELECT E.* from " + DBSchema + "EventLog E join EventLog1 E1 on E.MD1 = E1.MD1 and E.[DateTime] = E1.LTime) EV ON T.Id = EV.MD1" +
                " WHERE T.IsInMarket = 1 and EV.MD2 <> ''" +
                " AND T.Id NOT IN (Select TicketId from " + DBSchema + "TicketFlag where FlagDefinitionId = " + flagDefinitionId + ")" +
                " ) R where R.TotalClear > 0";
            context.log("get event log - Ticket_Go_To_Market" + queryGetEvent);
            new sql.Request().query(queryGetEvent, (err, result) => {
                context.log("this is --------> EventLog " + result.recordset.length);
                resolve(result.recordset);
            });
        });
    
        Promise.all([getAllEventTicketGoToMarket]).then(function(values) {
            var ticketGoToMarket = values[0];
            context.log("this is --------> values: " + values[0].length + " ==+++++==== " + JSON.stringify(values[0], null, 2));
    
            if (ticketGoToMarket.length != 0) {
                listTicketsFlag = _.filter(ticketGoToMarket, function(num) {
                    var countSP = num.MD2.split(',');
                    // context.log("countSP =====> " + countSP.length + "num.TotalClear ==>" + num.TotalClear)
                    if (num.TotalClear > countSP.length) {
                        return num.MD1;
                    }
                });
                // context.log("listTicketsFlag =====> " + JSON.stringify(listTicketsFlag, null, 2));
            }
            insertTicketFlag();
    
        });
    
        function insertTicketFlag() {
            context.log("this is ----- ===> Insert:  " + listTicketsFlag);
            // insert
            var insertTicketFlagPromise = new Promise(function(resolve, reject) {
    
                context.log("listTicketFlag ----- ===> " + listTicketsFlag.length);
    
                if (listTicketsFlag.length == 0) {
                    context.log(" -------------------- No have ticket need FLAG");
                    resolve();
    
                } else {
    
                    // insert new data to TicketFlag FlagTickets
                    var listTicketInsert = ""; //convertArrayToSQLString(listTicketsFlag, true, flagDefinitionId);
                    var len = listTicketsFlag.length - 1;
                    for (var j = 0; j <= len; j++) {
                        listTicketInsert += '(\'' + listTicketsFlag[j] + '\', \'' + flagDefinitionId + '\')';
                        if (j != len) {
                            listTicketInsert += ",";
                        }
                    }
                    context.log("HERE : " + listTicketInsert);
    
                    var insertQuery = 'Insert into  ' + DBSchema + '[TicketFlag] (TicketId, FlagDefinitionId) values ' + listTicketInsert + '';
    
                    context.log("this is --------> InsertQuery" + insertQuery);
    
                    // return;
    
                    context.log("read data of FlagRule");
                    new sql.Request().query(insertQuery, (err, result) => {
                        context.log("this is --------> insertQuery");
                        resolve(result);
    
                    });
                }
            });
    
            Promise.all([insertTicketFlagPromise]).then(function(values) {
                context.log("DONE ALL");
                sql.close();
                context.done();
            })
        }
    
    }).catch(function(err) {
        console.log(err);
        context.done();
    });
    

    };

    enter image description here

    enter image description here

    如何解决这个缓慢的问题?

    最佳答案

    我们的 node.js 函数也注意到了这一点。经过大量研究和测试,我们发现:

  • 功能应用程序在五分钟不活动后进入“冷”状态。当它们退出冷态时,您可以预期最多 10 秒的时间将节点 JavaScript 编译/转换为 .Net 代码,以便它们可以在更大的 Function 引擎中本地运行。请注意,我在上面说的是“功能应用程序”,而不仅仅是“功能”
  • 即使它处于“热”状态(即 < 5 分钟的空闲时间),有时该函数也会花费过多的时间来加载外部库
  • 这种性能下降的最大罪魁祸首是带有许多小文件的大型外部库。

  • 那么你能做些什么来缓解这种情况呢?以下是我们按复杂程度排序的内容:
  • 设置一个在不到 5 分钟的时间范围内执行的计时器功能。我们每四分钟运行一个简单的计时器,时间在 0 毫秒到 10 毫秒之间,您可以通过数学计算发现这是一种让您的函数应用程序处于热状态的非常便宜的方法。
  • 使用 Functions-Pack包以将所有外部库合并到一个文件中。当函数被重新编译或转译或那里发生任何魔术时,它会变得更快,因为它不必寻找数十个或数百个文件。
  • 使用 REST API 而不是 SDK 意味着需要零个外部库。最大的问题是生成授权 header ,Azure 中没有关于如何形成 Auth header 的标准,而且在大多数情况下,他们的这部分文档几乎没有涉及,尤其是 node.js。我曾考虑为生成各种 Azure Auth token 的 node.js 代码启动一个 github 存储库。
  • 将您的函数移植到 C#(是的,我对这个选项也不满意 - 我想要一个用于我们产品的全 JavaScript/TypeScript 平台)。尽管如此,删除交叉编译/编译/任何东西,它应该会大大加快速度。我现在将我们最复杂的函数之一移植到 C# 以进一步测试这个理论。
  • 迁移到应用服务计划似乎违背了 Azure Functions 的值(value)。我想要微软处理的无限规模和每次执行成本。应用服务计划迫使我再次考虑 CPU 和内存以及应用容量。

  • 这是 MSDN forums thread我发布请求就我们的问题提供反馈。

    关于javascript - Azure Function 执行速度极慢且不一致,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44878247/

    相关文章:

    javascript - HTML和jQuery-需要下拉至重叠元素

    javascript - jQuery在循环内生成ID

    configuration - Azure 配置的最佳实践?

    azure - Azure 上默认目录的租户 URL 是什么?

    mysql - 无法在 Azure Database for MySql 上创建存储过程

    javascript - 单击按钮后,无法禁用另一个按钮

    javascript - 如果在它们之前或之间有 await 调用,React 将不会在事件处理程序内部批量调用 setState

    azure - Azure Active Directory 是否具有 OAuth/OpenID Connect token 自检终结点?

    azure - 使用PowerShell和ServicePrincipal的AD用户的Azure SQL授权访问

    powershell - Azure SQL Powershell 命令的静音输出