J'essaie d'écrire une fonction Lambda à l'aide de Node.js qui se connecte à ma base de données RDS. La base de données fonctionne et est accessible à partir de mon environnement Elastic Beanstalk. Lorsque j'exécute la fonction, une erreur de délai d'attente est renvoyée.
J'ai essayé d'augmenter le délai d'attente jusqu'à 5 minutes avec exactement le même résultat.
La conclusion à laquelle j'en suis arrivé après quelques recherches est que c'est probablement un problème de sécurité mais que je n'ai pas trouvé la solution dans la documentation d'Amazon ou dans this answer (qui est le seul que j'ai pu trouver sur le sujet).
Voici les détails de sécurité:
Mon code est:
'use strict';
console.log("Loading getContacts function");
var AWS = require('aws-sdk');
var mysql = require('mysql');
exports.handler = (event, context, callback) => {
var connection = mysql.createConnection({
Host : '...',
user : '...',
password : '...',
port : 3306,
database: 'ebdb',
debug : false
});
connection.connect(function(err) {
if (err) callback(null, 'error ' +err);
else callback(null, 'Success');
});
};
Le résultat obtenu est:
"errorMessage": "2017-03-05T05:57:46.851Z 9ae64c49-0168-11e7-b49a-a1e77ae6f56c Task timed out after 10.00 seconds"
Je tiens à remercier tous ceux qui ont aidé, le problème s'est avéré différent de ce que je pensais. La callback
dans le code ne fonctionne pas pour une raison quelconque même si elle se trouve dans OWN DEFAULT SAMPLE d'Amazon.
Le code de travail ressemble à ceci:
'use strict';
console.log("Loading getContacts function");
var AWS = require('aws-sdk');
var mysql = require('mysql');
exports.handler = (event, context) => {
var connection = mysql.createConnection({
Host : '...',
user : '...',
password : '...',
port : 3306,
database: 'ebdb',
debug : false
});
connection.connect(function(err) {
if (err) context.fail();
else context.succeed('Success');
});
};
Bien que l'utilisation du contexte fonctionne, il vous suffit d'ajouter context.callbackWaitsForEmptyEventLoop = false;
au gestionnaire, puis d'utiliser le rappel comme d'habitude, comme ceci:
exports.handler = (event, context) => {
context.callbackWaitsForEmptyEventLoop = false;
var connection = mysql.createConnection({
//connection info
});
connection.connect(function(err) {
if (err) callback(err);
else callback(null, 'Success');
});
};
La réponse est ici dans la documentation (cela m'a pris quelques heures pour trouver ceci): http://docs.aws.Amazon.com/lambda/latest/dg/nodejs-prog-model-using-old -runtime.html
Dans la section "Comparaison des méthodes de contexte et de rappel", une note "Important" explique tout.
Au bas de la note, on peut lire:
Par conséquent, si vous souhaitez le même comportement que les méthodes de contexte, vous devez définir la propriété de l'objet de contexte, callbackWaitsForEmptyEventLoop, sur false.
En gros, le rappel se poursuit jusqu'à la fin de la boucle d'événement, contrairement au contexte qui termine la boucle d'événement. Par conséquent, si vous définissez callbackWaitsForEmptyEventLoop, le rappel fonctionne comme un contexte.
Le RDS et le Lambda appartiennent au même groupe de sécurité.
C'est la clé. Par défaut, la communication au sein du même groupe de sécurité n'est pas autorisée. Et vous devez l’autoriser explicitement (E.x sg-xxxxx ALL TCP). Cela ne fonctionnera que si votre lambda tente d’accéder à la base de données par adresse IP privée.
S'il essaie d'y accéder via une adresse IP publique, cela ne fonctionnera pas et vous devrez également percer les trous nécessaires.
Cependant, il existe une meilleure approche:
3306
dans RDS sg pour lambdas sg.Je partage mon expérience lors de la connexion à RDS.
Vous devez activer l'accès
VPC
pour leLambda function
, au cours duquel vous lui attribuerez un groupe de sécurité .
Ensuite, dans le groupe de sécurité attribué à l'instance RDS, vous activerez l'accès au groupe de sécurité affecté à la fonction Lambda.
Vous pouvez obtenir plus d'informations ici
Lors de la configuration initiale de la base de données, un groupe de sécurité sera automatiquement créé. par défaut, l'adresse IP avec laquelle vous avez configuré la base de données. Lorsque vous exécutez lambda, cette règle bloque le trafic. Consultez les journaux de vos erreurs de base de données et confirmez qu’il refuse la connexion.
***** could not be resolved: Name or service not known
Vous devez créer une règle dans le groupe de sécurité pour autoriser le trafic lambda. Accédez à la console de votre instance RDS et cliquez sur le groupe de sécurité, sélectionnez Inbound. Là, vous verrez les règles. Appelez ensuite pour vous ouvrir au monde, recherchez les adresses IP AWS lambda ou créez un VPC.
connection.end () devrait être après le rappel:
code de travail:
'use strict';
var mysql = require('mysql');
var connection = mysql.createConnection({
Host : 'xxxxxx.amazonaws.com',
user : 'testuser',
password : 'testPWD',
port : 3306,
database: 'testDB',
debug : false
});
module.exports.handler = (event, context, callback) => {
// **Connection to database**
connection.connect(function(err) {
if (err) {
console.error('Database connection failed: ' + err.stack);
context.fail();
return;
}
else{
console.log('Connected to database.');
}
});
connection.query('show tables from testDB', function (error, results, fields) {
if (error) {
console.log("error: connection failed with db!");
connection.destroy();
throw error;
} else {
// connected!
console.log("info: connection ok with db!");
console.log(results);
context.succeed("done");
callback(error, results);
}
});
//Send API Response
callback(null, {
statusCode: '200',
body: 'succeed',
headers: {
'Content-Type': 'application/json',
},
});
//Close Connection
connection.end(); // Missing this section will result in timeout***
};
Le problème ne provient pas du délai d'attente, mais de la façon dont vous fermez la connexion. Utilisez plutôt .destroy()
si vous ne voulez pas attendre le rappel que OR l'utilise correctement lors de la fermeture de la connexion dans .end(function(err) { //Now call your callback });
.
Voir ce fil de discussion pour une explication plus détaillée.
J'ai également fait face à un scénario de délai d'attente similaire. Le problème ne concernait pas connection.end()
après connection.connect()
. Connection.end()
devrait être fait avant callback
.
Code de travail:
var mysql = require('mysql');
var connection = mysql.createConnection({
Host : 'Host_name',
user : 'root',
password : 'password'
});
module.exports.handler = (event, context, callback) => {
// **Connection to database**
connection.connect(function(err) {
if (err) {
console.error('Database connection failed: ' + err.stack);
return;
}
console.log('Connected to database.');
});
// **Hit DB Query**
connection.query("Query", function(err, rows, fields) {
console.log(rows);
});
//**Close Connection**
connection.end(); ***// Missing this section will result in timeout***
//**Send API Response**
callback(null, {
statusCode: '200',
body: "Success",
headers: {
'Content-Type': 'application/json',
},
});
};