Porównaj ceny domen i usług IT, sprzedawców z całego świata

Jak połączyć się z instancją Redis za pomocą funkcji AWS Lambda?


Próbuję utworzyć interfejs API dla pojedynczej strony aplikacji internetowej przy użyciu

AWS Lambda
https://aws.amazon.com/lambda/
i

framework bezserwerowy
https://github.com/serverless/serverless... Chcę użyć

Redis Cloud
https://redislabs.com/redis-cloud
do przechowywania, głównie ze względu na połączenie szybkości i trwałości danych. W przyszłości mogę korzystać z większej liczby funkcji Redis Cloud, więc wolałbym nie używać do tego ElastiCache. Moja instancja Redis Cloud działa w tym samym regionie AWS, co moja funkcja.
Mam funkcję
related
, która pobiera hashtag z żądania GET do punktu końcowego API i sprawdza, czy istnieje odpowiedni wpis w bazie danych. Jeśli tak, powinien natychmiast zwrócić wyniki. Jeśli nie, powinien poprosić

RiteTag
https://ritetag.com/, zapisz wyniki w Redis, a następnie zwróć je użytkownikowi.
Jestem całkiem nowy w tej dziedzinie, więc prawdopodobnie robię coś uroczo naiwnego. Oto program obsługi zdarzeń:
'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)
}
})
}

Oto plik
../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)
}
})
}
})
}
}
})
})
}

To wszystko działa do pewnego stopnia zgodnie z oczekiwaniami. Jeśli uruchomię funkcję lokalnie (używając
sls function run related
) nie mam problemu - tagi są odczytywane i zapisywane w bazie danych Redis tak, jak powinny. Jednak po wdrożeniu (przy użyciu
sls dash deploy
) działa w

pierwszy raz uruchamia się po wdrożeniu

a następnie przestaje działać. Wszystkie kolejne próby uruchomienia go po prostu zwracają
null
do przeglądarki (lub Postman, curl lub aplikacji internetowej). To prawda, niezależnie od tego, czy tag, którego używam do testowania, jest już w bazie danych, czy nie. Jeśli następnie wdrożę ponownie bez wprowadzania zmian w samej funkcji, będzie ona działać ponownie - raz.
Na moim komputerze lokalnym funkcja najpierw rejestruje
Connected: true
w konsoli, następnie wyniki zapytania, a następnie
Connection closed.
na AWS loguje
Connected: true
, następnie wyniki zapytania i to wszystko. Przy drugim uruchomieniu rejestruje
Połączenie zamknięte.
i nic więcej. Przy trzecim i wszystkich kolejnych uruchomieniach w ogóle nic nie rejestruje. Żadne środowisko nie zgłasza żadnych błędów.
Wydaje się całkiem jasne, że problem dotyczy połączenia Redis. Jeśli nie zamknę go w callbackach, kolejne próby wywołania funkcji po prostu wygasną. Próbowałem też użyć
redis.unref
zamiast
redis.quit
, ale nie miało to żadnego znaczenia.
Każda pomoc byłaby bardzo mile widziana.
Zaproszony:
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Teraz rozwiązałem swój własny problem i mam nadzieję, że mogę pomóc komuś, kto napotka ten problem w przyszłości.
Podczas łączenia się z bazą danych należy wziąć pod uwagę dwie główne kwestie, tak jak to zrobiłem w powyższym kodzie z funkcji Lambda:
  • Po wywołaniu
    context.succeed ()
    ,
    context.fail ()
    lub
    context.done ()
    AWS może zamrozić wszystkie procesy, które nie są jeszcze zakończone ... To właśnie spowodowało, że AWS zarejestrował
    Połączenie zamknięte
    przy drugim wywołaniu mojego punktu końcowego API - proces został zamrożony tuż przed zakończeniem zamykania Redis, a następnie odmrożony przy następnym wywołaniu, po czym kontynuował od miejsca, w którym został przerwany , mówiąc, że połączenie jest zamknięte. Jeśli chcesz zamknąć połączenie z bazą danych, upewnij się, że jest całkowicie zamknięte, przed niż wywołać jedną z tych metod. Możesz to zrobić, umieszczając wywołanie zwrotne w programie obsługi zdarzeń, który jest uruchamiany po zamknięciu połączenia (w moim przypadku
    .on ('end')
    ).
  • Jeśli podzielisz swój kod na oddzielne pliki i
    wymagasz
    na górze każdego pliku, tak jak ja, Amazon będzie buforował jak najwięcej tych modułów w pamięci. Jeśli powoduje to problemy, spróbuj przenieść wywołania
    require ()
    wewnątrz funkcji, a nie u góry pliku, a następnie wyeksportuj tę funkcję. Te moduły będą następnie ponownie importowane za każdym razem, gdy funkcja zostanie uruchomiona.

Oto mój zaktualizowany kod. Zauważ, że umieściłem również moją konfigurację Redis w osobnym pliku, aby móc zaimportować ją do innych funkcji Lambda bez powielania kodu.

Obsługa zdarzeń
>
'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)
}
})
}


Konfiguracja Redis
>
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))
}


Funkcjonować
>
'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
})
}
})
}
})
}
}
})
})
}

Działa dokładnie tak, jak powinno - i bardzo szybko.

Aby odpowiedzieć na pytania, Zaloguj się lub Zarejestruj się