web-dev-qa-db-ja.com

このHTTPリクエストがAWS Lambdaで機能しないのはなぜですか?

AWS Lambdaを使い始めて、ハンドラー関数から外部サービスをリクエストしようとしています。 この回答 によると、HTTPリクエストはうまく機能するはずです。そうでないと言うドキュメントは見つかりませんでした。 (実際、人々は投稿した SMSを送信するためにTwilio APIを使用するコード 。)

私のハンドラーコードは次のとおりです。

var http = require('http');

exports.handler = function(event, context) {
  console.log('start request to ' + event.url)
  http.get(event.url, function(res) {
    console.log("Got response: " + res.statusCode);
  }).on('error', function(e) {
    console.log("Got error: " + e.message);
  });

  console.log('end request to ' + event.url)
  context.done(null);
}

cloudWatchログに次の4行が表示されます。

2015-02-11 07:38:06 UTC START RequestId: eb19c89d-b1c0-11e4-bceb-d310b88d37e2
2015-02-11 07:38:06 UTC eb19c89d-b1c0-11e4-bceb-d310b88d37e2 start request to http://www.google.com
2015-02-11 07:38:06 UTC eb19c89d-b1c0-11e4-bceb-d310b88d37e2 end request to http://www.google.com
2015-02-11 07:38:06 UTC END RequestId: eb19c89d-b1c0-11e4-bceb-d310b88d37e2

私はそこに別の行を期待しています:

2015-02-11 07:38:06 UTC eb19c89d-b1c0-11e4-bceb-d310b88d37e2 Got response: 302

しかし、それは欠落しています。ローカルマシンのノードでハンドラーラッパーなしで重要な部分を使用している場合、コードは期待どおりに機能します。

私が使用しているinputfile.txtinvoke-async呼び出し用です:

{
   "url":"http://www.google.com"
}

リクエストを行うハンドラーコードの部分は完全にスキップされているようです。 request lib から始め、単純なhttpを使用して最小限の例を作成しました。また、ログを確認するために、制御するサービスのURLを要求しようとしましたが、要求はありません。

私は完全に困惑しています。 NodeやAWS LambdaがHTTPリクエストを実行しない理由はありますか?

70
awendt

もちろん、私は問題を誤解していました。 AWS自身が言うとおり

Lambdaで初めてnodejsに遭遇した場合、よくあるエラーは、コールバックが非同期に実行されることを忘れ、別のコールバック(S3.PUT操作など)が完了するのを本当に待ちたいときに元のハンドラーでcontext.done()を呼び出すことです。作業が不完全な状態で終了する関数。

要求のコールバックが発生する前にcontext.done wayを呼び出していたため、事前に関数が終了していました。

作業コードは次のとおりです。

var http = require('http');

exports.handler = function(event, context) {
  console.log('start request to ' + event.url)
  http.get(event.url, function(res) {
    console.log("Got response: " + res.statusCode);
    context.succeed();
  }).on('error', function(e) {
    console.log("Got error: " + e.message);
    context.done(null, 'FAILURE');
  });

  console.log('end request to ' + event.url);
}

Update:2017年以降、AWSは古いNodejs 0.10を廃止し、新しい4.3ランタイムのみが利用可能になりました(古い関数を更新する必要があります)。このランタイムにより、ハンドラー関数にいくつかの変更が導入されました。新しいハンドラーには3つのパラメーターがあります。

function(event, context, callback)

コンテキストパラメータでsucceeddonefailを引き続き見つけることができますが、AWSではcallback関数を使用することをお勧めします。そうしないと、デフォルトでnullが返されます。

callback(new Error('failure')) // to return error
callback(null, 'success msg') // to return ok

完全なドキュメントは http://docs.aws.Amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html にあります。

69
awendt

ええ、Awendtの答えは完璧です。作業コードを表示します... reqPost.end();行の直後にcontext.succeed( 'Blah');行がありました。以下に示す場所に移動すると、すべてが解決しました。

console.log('GW1');

var https = require('https');

exports.handler = function(event, context) {

    var body='';
    var jsonObject = JSON.stringify(event);

    // the post options
    var optionspost = {
        Host: 'the_Host',
        path: '/the_path',
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
        }
    };

    var reqPost = https.request(optionspost, function(res) {
        console.log("statusCode: ", res.statusCode);
        res.on('data', function (chunk) {
            body += chunk;
        });
        context.succeed('Blah');
    });

    reqPost.write(jsonObject);
    reqPost.end();
};
11
imTachu

リクエストを行うさまざまな方法について、ウェブ上で多くの投稿を見つけましたが、AWS Lambdaでレスポンスを同期的に処理する方法を実際に示すものはありません。

Httpsリクエストを使用し、応答の本文全体を収集して返すNode 6.10.3ラムダ関数を次に示します。結果をリストされていない関数processBodyに制御を渡します。このコードでは、httpとhttpsは互換性があると思います。

私は asyncユーティリティモジュール を使用しています。これは初心者にとって理解しやすいものです。それを使用するには、AWS Stackにプッシュする必要があります( serverless framework をお勧めします)。

データはチャンク単位で返され、グローバル変数に収集され、最後にデータにendedが含まれるときにコールバックが呼び出されることに注意してください。

'use strict';

const async = require('async');
const https = require('https');

module.exports.handler = function (event, context, callback) {

    let body = "";
    let countChunks = 0;

    async.waterfall([
        requestDataFromFeed,
        // processBody,
    ], (err, result) => {
        if (err) {
            console.log(err);
            callback(err);
        }
        else {
            const message = "Success";
            console.log(result.body);
            callback(null, message);
        }
    });

    function requestDataFromFeed(callback) {
        const url = 'https://put-your-feed-here.com';
        console.log(`Sending GET request to ${url}`);
        https.get(url, (response) => {
            console.log('statusCode:', response.statusCode);
            response.on('data', (chunk) => {
                countChunks++;
                body += chunk;
            });
            response.on('end', () => {
                const result = {
                    countChunks: countChunks,
                    body: body
                };
                callback(null, result);
            });
        }).on('error', (err) => {
            console.log(err);
            callback(err);
        });
    }
};
3
Zodman

私はまったく同じ問題を抱えていましたが、NodeJSでのプログラミングはJavaScriptに基づいているため、実際にPythonやJavaとは異なることに気付きました。興味があるか、この質問に来るかもしれない少数の新しい人々があるかもしれないので、私は単純な概念を使用しようとします。

次のコードを見てみましょう。

var http = require('http'); // (1)
exports.handler = function(event, context) {
  console.log('start request to ' + event.url)
  http.get(event.url,  // (2)
  function(res) {  //(3)
    console.log("Got response: " + res.statusCode);
    context.succeed();
  }).on('error', function(e) {
    console.log("Got error: " + e.message);
    context.done(null, 'FAILURE');
  });

  console.log('end request to ' + event.url); //(4)
}

Httpパッケージ(1)のメソッドを呼び出すたびに、イベントとして作成され、このイベントは別のイベントを取得します。 「get」関数(2)は、実際にはこの別個のイベントの開始点です。

これで、(3)の関数は別のイベントで実行され、コードはパスの実行を継続し、(4)に直接ジャンプして終了します。これ以上の作業はありません。

ただし、(2)で発生したイベントはまだどこかで実行されており、終了するのに独自の甘い時間がかかります。かなり奇妙ですよね?まあ、そうではありません。これがNodeJSの仕組みであり、この概念に頭を包むことは非常に重要です。これは、JavaScript Promisesが支援する場所です。

JavaScript Promiseの詳細については、こちらをご覧ください こちら 。簡単に言えば、コードの実行をインラインに保ち、新しい/余分なスレッドを生成しないJavaScript Promiseが必要になります。

一般的なNodeJSパッケージのほとんどには、使用可能なAPIの約束バージョンがありますが、BlueBirdJSのような他のアプローチも同様の問題に対処しています。

上記で作成したコードは、次のように大まかに書き直すことができます。

'use strict';
console.log('Loading function');
var rp = require('request-promise');
exports.handler = (event, context, callback) => {    

    var options = {
    uri: 'https://httpbin.org/ip',
    method: 'POST',
    body: {

    },
    json: true 
};


    rp(options).then(function (parsedBody) {
            console.log(parsedBody);
        })
        .catch(function (err) {
            // POST failed... 
            console.log(err);
        });

    context.done(null);
};

AWS Lambdaにインポートする場合、上記のコードは直接機能しないことに注意してください。 Lambdaの場合、モジュールもコードベースでパッケージ化する必要があります。

3
mmansoor

ノードを使用したHttp要求の簡単な作業例。

const http = require('https')
exports.handler = async (event) => {
    return httprequest().then((data) => {
        const response = {
            statusCode: 200,
            body: JSON.stringify(data),
        };
    return response;
    });
};
function httprequest() {
     return new Promise((resolve, reject) => {
        const options = {
            Host: 'jsonplaceholder.typicode.com',
            path: '/todos',
            port: 443,
            method: 'GET'
        };
        const req = http.request(options, (res) => {
          if (res.statusCode < 200 || res.statusCode >= 300) {
                return reject(new Error('statusCode=' + res.statusCode));
            }
            var body = [];
            res.on('data', function(chunk) {
                body.Push(chunk);
            });
            res.on('end', function() {
                try {
                    body = JSON.parse(Buffer.concat(body).toString());
                } catch(e) {
                    reject(e);
                }
                resolve(body);
            });
        });
        req.on('error', (e) => {
          reject(e.message);
        });
        // send the request
       req.end();
    });
}
0
smsivaprakaash