AWS Lambda および Serverless Framework を使用して、単一ページのWebアプリ用のAPIを構築しようとしています。ストレージに Redis Cloud を使用したいのですが、主に速度とデータの永続性の組み合わせに使用します。将来的にはより多くのRedis Cloud機能を使用する可能性があるため、このためにElastiCacheの使用を避けたいと思います。私のRedis Cloudインスタンスは、私の機能と同じAWSリージョンで実行されています。
GETリクエストからAPIエンドポイントへのハッシュタグを取得し、データベースにエントリがあるかどうかを確認するrelated
という関数があります。存在する場合は、すぐに結果が返されます。そうでない場合は、 RiteTag をクエリし、結果をRedisに書き込み、結果をユーザーに返す必要があります。
私はこれにかなり慣れていないので、たぶん愛らしい素朴なことをしているでしょう。イベントハンドラは次のとおりです。
'use strict'
const lib = require('../lib/related')
module.exports.handler = function (event, context) {
lib.respond(event, (err, res) => {
if (err) {
return context.fail(err)
} else {
return context.succeed(res)
}
})
}
../lib/related.js
ファイルは次のとおりです。
var redis = require('redis')
var jsonify = require('redis-jsonify')
var rt = require('./ritetag')
var redisOptions = {
Host: process.env.REDIS_URL,
port: process.env.REDIS_PORT,
password: process.env.REDIS_PASS
}
var client = jsonify(redis.createClient(redisOptions))
module.exports.respond = function (event, callback) {
var tag = event.hashtag.replace(/^#/, '')
var key = 'related:' + tag
client.on('connect', () => {
console.log('Connected:', client.connected)
})
client.on('end', () => {
console.log('Connection closed.')
})
client.on('ready', function () {
client.get(key, (err, res) => {
if (err) {
client.quit()
callback(err)
} else {
if (res) {
// Tag is found in Redis, so send results directly.
client.quit()
callback(null, res)
} else {
// Tag is not yet in Redis, so query Ritetag.
rt.hashtagDirectory(tag, (err, res) => {
if (err) {
client.quit()
callback(err)
} else {
client.set(key, res, (err) => {
if (err) {
callback(err)
} else {
client.quit()
callback(null, res)
}
})
}
})
}
}
})
})
}
これはすべて、ある程度までは期待どおりに機能します。関数をローカルで(sls function run related
を使用して)実行する場合、問題はまったくありません。タグは、必要に応じてRedisデータベースから読み書きされます。ただし、(sls dash deploy
を使用して)デプロイすると、デプロイ後に初めて動作し、動作しなくなります。それを実行しようとするそれ以降のすべての試みは、ブラウザに(またはPostman、curl、またはWebアプリに)null
を返すだけです。これは、テストに使用するタグが既にデータベースにあるかどうかに関係なく当てはまります。その後、機能自体に変更を加えずに再デプロイすると、再び機能します。
私のローカルマシンでは、関数はまずConnected: true
をコンソールに記録し、次にクエリの結果を記録し、次にConnection closed.
AWSでConnected: true
を記録し、次にクエリの結果を記録します。それでおしまい。 2回目の実行では、Connection closed.
のみをログに記録します。 3回目以降のすべての実行では、何も記録されません。どちらの環境もエラーを報告しません。
問題はRedisへの接続にあることは明らかです。コールバックで閉じない場合、その後の関数呼び出しの試行はタイムアウトになります。 redis.unref
の代わりにredis.quit
を使用してみましたが、それは何の違いももたらさないようです。
どんな助けも大歓迎です。
私は今、自分の問題を解決しました。将来、この問題を経験している人の助けになることを願っています。
上記のコードでLambda関数から行ったように、データベースに接続するときの2つの主要な考慮事項があります。
context.succeed()
、context.fail()
、またはcontext.done()
が呼び出されると、AWSはまだ終了していないプロセスをフリーズする場合があります。これが、AWSがAPIエンドポイントへの2回目の呼び出しで_Connection closed
_をログに記録した原因です。Redisが終了する直前にプロセスがフリーズし、次の呼び出しで解凍されました。接続が閉じられたことを報告します。テイクアウト:データベース接続を閉じたい場合は、完全に閉じていることを確認してくださいbeforeこれらのメソッドの1つを呼び出します。これを行うには、接続のクローズ(私の場合は.on('end')
)によってトリガーされるイベントハンドラーにコールバックを配置します。require
した場合、私がしたように、Amazonはそれらのモジュールをできるだけ多くメモリにキャッシュします。問題が発生する場合は、require()
呼び出しをファイルの先頭ではなく関数内に移動してから、その関数をエクスポートしてみてください。これらのモジュールは、関数が実行されるたびに再インポートされます。これが私の更新されたコードです。また、Redis構成を別のファイルに入れているため、コードを複製せずに他のLambda関数にインポートできることに注意してください。
_'use strict'
const lib = require('../lib/related')
module.exports.handler = function (event, context) {
lib.respond(event, (err, res) => {
if (err) {
return context.fail(err)
} else {
return context.succeed(res)
}
})
}
_
_module.exports = () => {
const redis = require('redis')
const jsonify = require('redis-jsonify')
const redisOptions = {
Host: process.env.REDIS_URL,
port: process.env.REDIS_PORT,
password: process.env.REDIS_PASS
}
return jsonify(redis.createClient(redisOptions))
}
_
_'use strict'
const rt = require('./ritetag')
module.exports.respond = function (event, callback) {
const redis = require('./redis')()
const tag = event.hashtag.replace(/^#/, '')
const key = 'related:' + tag
let error, response
redis.on('end', () => {
callback(error, response)
})
redis.on('ready', function () {
redis.get(key, (err, res) => {
if (err) {
redis.quit(() => {
error = err
})
} else {
if (res) {
// Tag is found in Redis, so send results directly.
redis.quit(() => {
response = res
})
} else {
// Tag is not yet in Redis, so query Ritetag.
rt.hashtagDirectory(tag, (err, res) => {
if (err) {
redis.quit(() => {
error = err
})
} else {
redis.set(key, res, (err) => {
if (err) {
redis.quit(() => {
error = err
})
} else {
redis.quit(() => {
response = res
})
}
})
}
})
}
}
})
})
}
_
これは本来どおりに機能します。また、非常に高速です。