Скорость выполнения функций Azure очень медленная и непоследовательная.

Я пишу несколько сценариев функций Azure, которые читают и записывают из / во внутреннюю базу данных и отображают соответствующую информацию на веб-странице.

Я заметил чрезмерную медлительность или даже тайм-аут в веб-интерфейсе при загрузке веб-страницы, которая вызывает сценарий функции Azure. После дальнейшего расследования я понял, что следующее:

  1. Скриптам функций Azure иногда требуется от 10 секунд до более 1 минуты для подключения к базе данных SQL.
  2. Иногда сценарии запускаются за несколько миллисекунд, а иногда для полного запуска сценария требуется более 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();
});

};

введите описание изображения здесь

введите описание изображения здесь

Как решить эту проблему с медлительностью?


person Eyad    schedule 03.07.2017    source источник
comment
Обратите внимание, что выполнение SQL-запроса, написанного в скрипте функции Azure в Visual Studio или SQL Management Studio, выполняется очень быстро. Итак, я ничего не подозреваю относительно производительности SQL-запроса.   -  person Eyad    schedule 03.07.2017
comment
Какой тариф вы используете?   -  person CSharpRocks    schedule 03.07.2017
comment
@CSharpRocks WestEuropePlan (Потребление)   -  person Eyad    schedule 03.07.2017
comment
Вам нужно попробовать переключиться на план службы приложений, чтобы увидеть, будет ли у вас такое же поведение?   -  person CSharpRocks    schedule 03.07.2017
comment
Поскольку задержка возникает из-за метода подключения библиотеки mssql, вам следует попытаться воспроизвести эту проблему с подключением за пределами Функций Azure, чтобы определить, связана ли это с библиотекой mssql или с тем, как вы ее используете, или, возможно, ваши подключения к базе данных регулируются.   -  person mathewc    schedule 03.07.2017
comment
@CSharpRocks Это сработало после того, как я изменил план на план службы приложений. Пожалуйста, добавьте свой комментарий в качестве ответа ниже, чтобы принять его.   -  person Eyad    schedule 12.07.2017
comment
Были проблемы с холодным запуском функций, основанных на потреблении, если они давно не вызывались.   -  person snowCrabs    schedule 23.08.2017


Ответы (1)


Мы заметили это и с нашими функциями node.js. После долгих исследований и испытаний мы обнаружили следующее:

  1. Функция Приложения переходят в «холодное» состояние после пяти минут бездействия. Когда они выходят из холодного состояния, вы можете ожидать до 10 секунд того, что кажется компиляцией / транспиляцией узлового JavaScript в код .Net, чтобы они могли работать изначально внутри большего движка функций. Обратите внимание, что выше я сказал «Приложение-функция», а не просто «Функция».

  2. Даже если он находится в «горячем» состоянии (т.е. ‹5 минут простоя), бывают случаи, когда функции требуется слишком много времени для загрузки внешних библиотек.

  3. Самым большим виновником этого снижения производительности являются более крупные внешние библиотеки с большим количеством небольших файлов.

Итак, что вы можете сделать, чтобы облегчить это? Вот что мы сделали в порядке сложности:

  1. Настройте функцию таймера, которая будет работать в течение менее 5 минут. Мы запускаем простой таймер каждые четыре минуты, который занимает от 0 до 10 мсек, и вы можете посчитать, что это ОЧЕНЬ дешевый способ поддерживать приложение-функцию в горячем состоянии.

  2. Используйте пакет Functions-Pack для объединения всех ваших внешних библиотек в один файл. Когда функция повторно компилируется или транспилируется, или что-то еще происходит там, она становится намного быстрее, поскольку ей не нужно искать десятки или сотни файлов.

  3. Использование REST API вместо SDK означает, что не требуется никаких внешних библиотек. Большая проблема с этим - создание заголовков авторизации, в Azure нет стандарта для формирования заголовка Auth, и эта часть их документации почти не покрывается в большинстве случаев, особенно с node.js. Я думал о запуске репозитория github исключительно для кода node.js, который генерирует различные токены аутентификации Azure.

  4. Перенесите свои функции на C # (да, меня тоже не устраивает этот вариант - мне нужна платформа, полностью использующая JavaScript / TypeScript для нашего продукта). Тем не менее, удалите кросс-компиляцию / транспиляцию / что-то еще, и это должно значительно ускориться. Я сейчас портирую одну из наших самых сложных функций на C #, чтобы дополнительно проверить эту теорию.

  5. Переход к плану службы приложений кажется нелогичным по сравнению с ценностью Функций Azure. Мне нужен неограниченный масштаб, который обслуживает Microsoft, и стоимость выполнения. План службы приложений заставляет меня снова думать о процессорах, памяти и емкости приложений.

Вот ветка форумов MSDN, которую я опубликовал с просьбой дать отзыв о наших проблемах.

person Graham    schedule 28.08.2017
comment
Прошло некоторое время с тех пор, как вы ответили, переносили ли вы свои функции на C # и стоило ли оно того? Как дела? - person strattonn; 12.06.2019
comment
Нет, мы не хотели начинать использовать C # и сохранили все на JavaScript. Теперь у Azure есть премиальное предложение, позволяющее всегда держать экземпляр функции включенным. - person Graham; 12.06.2019
comment
Для нас даже небольшое приложение, написанное на .NET Core 3.1, полностью асинхронно, плохо себя ведет в тарифных планах «Потребление» и «Премиум». Мы достигли приемлемого уровня производительности в рамках плана службы приложений с довольно сильным горизонтальным масштабированием (например, пять 8-ядерных компьютеров по 32 ГБ для обслуживания около 250 запросов в секунду). Мы пробовали развертывание как под Windows, так и под Linux с небольшим увеличением производительности под Linux. Мне кажется, что если вам редко нужна мощность и посредственный уровень производительности, вы можете выбрать Функции Azure. Если вам нужны реальные вещи, подумайте о службах приложений вместо функций Azure. - person Stefan Iancu; 17.12.2020