Возвращение обещаний не будет работать для AWS SDK - PullRequest
0 голосов
/ 21 марта 2020

Я создал API, который вызывает API-интерфейс cloudWatch AWS и возвращает точки данных, которые можно отобразить в моем приложении. У меня есть отдельные маршруты для каждого пакета (как показано в коде маршрутизации ниже). Этот API использует метод REST MVC.

Итак, пара вещей, которые я делаю со своей функцией.

  1. Чтение данных экземпляра EC2 из базы данных SQLite3 для захвата информация о работающем экземпляре (IP, instance_id, instance_launchtime), чтобы я мог поместить его в параметры, необходимые для API getMetricStatistics из AWS SDK.

  2. Эти данные из шага 1 затем поместите в массив параметров (3, которые отвечают 3 различными метрическими c точками данных). Это перебирает каждый параметр, вставляя его в API getMetricStatistics (ONE BY ONE SINCE getMetricStatistics не принимает сразу несколько метрик), чтобы получить точки данных для этого экземпляра и вывести их sh в массив.

Поскольку база данных асинхронна c Я полагаю, поэтому я приложил к ней обещание. Когда я загружаю конечную точку в свой браузер, он просто продолжает загружаться и не показывает никаких данных. Когда я делаю refre sh страницу, она показывает все результаты правильно ...

Это мой контроллер для API:

// Return results sent from Cloud Watch API
const InsightModel = require('../models/insight.model.js');
const cloudWatch = InsightModel.cloudWatch;
const CWParams = InsightModel.CWParams;
const packageById = InsightModel.packageById;

let cpuUtilParam;
let cpuCBParam;
let cpuCUParam;
let insightParams = [];
let metricResults = [];
exports.getAnalytics = (req, res) => {
  const currentDate = new Date().toISOString();
  let promise1 = new Promise((resolve, reject) => {
    packageById(req.params.packageKey, (err, data) => {
      if (err) {
        reject(
          res.status(500).send({
            message:
              err.message ||
              'Error while getting the insight configuration data.',
          })
        );
      } else {
        cpuUtilParam = new CWParams(
          currentDate,
          'CPUUtilization',
          'AWS/EC2',
          data[0].launch_time,
          data[0].instance_id
        );
        cpuCBParam = new CWParams(
          currentDate,
          'CPUCreditBalance',
          'AWS/EC2',
          data[0].launch_time,
          data[0].instance_id
        );
        cpuCUParam = new CWParams(
          currentDate,
          'CPUCreditUsage',
          'AWS/EC2',
          data[0].launch_time,
          data[0].instance_id
        );
        insightParams = [cpuUtilParam, cpuCBParam, cpuCUParam];
          resolve(insightParams);
      }
    });
  })
  let promise2 = new Promise((resolve, reject) => {
    insightParams.forEach(metric => {
      cloudWatch.getMetricStatistics(metric, function(err, data) {
        if (err) {
          reject(
            res.status(500).send({
              messaage:
                err.message ||
                'Error occured while running cloudWatch getMetricStatistcs API: ',
            })
          );
        } else {
          metricResults.push(data);
          if (metricResults.length === insightParams.length)
            resolve(metricResults);
        }
      });
    });
  });

  Promise.all([promise1, promise2])
    .then(metricResults => {
      res.send(metricResults);
      console.log('AWS CW API successful');
    })
    .catch(err => {
      res.status(500).send({
        messaage:
          err.message ||
          'Error occured while reading in a promise from cloudWatch getMetricStatistcs API: ',
      })
    });
  metricResults = [];
};

Модель для API:

// Call AWS Cost Explorer API
const AWS = require('aws-sdk');
const config = require('./AWSconfig');
const database = require('./db');

const insightdb = database.insightdb;

AWS.config.update({
  accessKeyId: config.accessKeyId,
  secretAccessKey: config.secretAccessKey,
  region: config.region,
});

//Linking AWS CloudWatch Service
var cloudWatch = new AWS.CloudWatch();

const packageById = (packageId, callback) => {
  insightdb.all(
    'SELECT * FROM ec2Instance WHERE package_id == ?',
    packageId,
    (err, rows) => {
      if (err) {
        callback(err, null);
      } else {
        callback(null, rows);
      }
    }
  );
};

// Parameter class to feed into the CloudWatch getMetricStatistics function
const CWParams = function(reqDate, metricName,service,launchTime,instanceId) {
  (this.EndTime = reqDate) /* required */,
    (this.MetricName = metricName) /* required */,
    (this.Namespace = service) /* required */,
    (this.Period = 3600) /* required */,
    (this.StartTime = launchTime) /* ${createDate}`, required */,
    (this.Dimensions = [
      {
        Name: 'InstanceId' /* required */,
        Value: instanceId /* required */,
      },
    ]),
    (this.Statistics = ['Maximum']);
};

//Exports variables to the controller (so they can be re-used)
module.exports = { cloudWatch, CWParams, packageById };

Маршрут для API:

module.exports = app => {
  const insight = require('../controllers/insight.controller.js');
  app.get('/insights/aws/:packageKey', insight.getAnalytics);
};

1 Ответ

1 голос
/ 22 марта 2020

Во втором конструкторе Promise гарантировано, что insightParams еще не был составлен, поскольку insightParams = [.....] находится в обратном вызове, который вызывается асинхронно. Следовательно, поток программы должен гарантировать, что все вещи «обещания 2» происходят только после выполнения «обещания1».

В коде более высокого уровня все становится намного проще, если асинхронные функции «обещают» при минимально возможном уровень. Сделайте две вещи в модели:

  • Обещание cloudWatch.getMetricStatistics()
  • Напишите packageById (), чтобы он возвращал Promise вместо принятия обратного вызова.

Модель, таким образом, становится:

const AWS = require('aws-sdk'); // no change
const config = require('./AWSconfig'); // no change
const database = require('./db'); // no change

const insightdb = database.insightdb; // no change

AWS.config.update({
    accessKeyId: config.accessKeyId,
    secretAccessKey: config.secretAccessKey,
    region: config.region
}); // no change

var cloudWatch = new AWS.CloudWatch(); // no change

// Promisify cloudWatch.getMetricStatistics() as  cloudWatch.getMetricStatisticsAsync().
cloudWatch.getMetricStatisticsAsync = (metric) => {
    return new Promise((resolve, reject) => {
        cloudWatch.getMetricStatistics(metric, function(err, data) {
            if (err) {
                if(!err.message) { // Probably not necessary but here goes ...
                    err.message = 'Error occured while running cloudWatch getMetricStatistcs API: ';
                }
                reject(err); // (very necessary)
            } else {
                resolve(data);
            }
        });
    });
};

// Ensure that packageById() returns Promise rather than accepting a callback.
const packageById = (packageId) => {
    return new Promise((resolve, reject) => {
        insightdb.all('SELECT * FROM ec2Instance WHERE package_id == ?', packageId, (err, rows) => {
            if (err) {
                reject(err);
            } else {
                resolve(rows);
            }
        });
    });
};

Теперь getAnalytics() можно записать так:

exports.getAnalytics = (req, res) => {
    packageById(req.params.packageKey)
    .then(data => {
        const currentDate = new Date().toISOString();
        let insightParams = [
            new CWParams(currentDate, 'CPUUtilization', 'AWS/EC2', data[0].launch_time, data[0].instance_id),
            new CWParams(currentDate, 'CPUCreditBalance', 'AWS/EC2', data[0].launch_time, data[0].instance_id),
            new CWParams(currentDate, 'CPUCreditUsage', 'AWS/EC2', data[0].launch_time, data[0].instance_id)
        ];
        // Composition of `insightParams` is synchronous so you can continue 
        // with the `cloudWatch.getMetricStatisticsAsync()` stuff inside the same .then().
        return Promise.all(insightParams.map(metric => cloudWatch.getMetricStatisticsAsync(metric))); // Simple because of the Promisification above.
    }, err => {
        // This callback handles error from packageById() only,
        // and is probably unnecessary but here goes ...
        if(!err.message) {
            err.message = 'Error while getting the insight configuration data.';
        }
        throw err;
    })
    .then(metricResults => {
        res.send(metricResults);
        console.log('AWS CW API successful');
    })
    .catch(err => {
        // Any async error arising above will drop through to here.
        res.status(500).send({
            'message': err.message
        }));
    });
};

Обратите внимание, что множественные захваты, каждый с res.status(500).send(), не нужны. Распространение ошибок по цепочке Promise допускает один терминал .catch()

...