获取我们在 Firebase 峰会上发布的所有信息,了解 Firebase 可如何帮助您加快应用开发速度并满怀信心地运行应用。了解详情

Spróbuj ponownie wykonać funkcje asynchroniczne

W tym dokumencie opisano, jak można zażądać asynchronicznych (nie HTTPS) funkcji działających w tle, aby ponowić próbę w przypadku niepowodzenia.

Semantyka ponawiania

Cloud Functions gwarantuje co najmniej jednokrotne wykonanie funkcji sterowanej zdarzeniami dla każdego zdarzenia emitowanego przez źródło zdarzenia. Jednak domyślnie, jeśli wywołanie funkcji zakończy się błędem, funkcja nie zostanie ponownie wywołana, a zdarzenie zostanie usunięte. Gdy włączysz ponawianie prób w funkcji sterowanej zdarzeniami, Cloud Functions będzie ponawiać nieudane wywołanie funkcji do czasu jego pomyślnego zakończenia lub wygaśnięcia okna ponawiania prób (domyślnie po 7 dniach).

Gdy ponawianie prób nie jest włączone dla funkcji, co jest ustawieniem domyślnym, funkcja zawsze zgłasza, że ​​została wykonana pomyślnie, aw jej dziennikach może pojawić się 200 OK . Dzieje się tak nawet wtedy, gdy funkcja napotkała błąd. Aby było jasne, kiedy funkcja napotka błąd, należy odpowiednio zgłosić błędy .

Dlaczego funkcje sterowane zdarzeniami nie kończą się?

W rzadkich przypadkach funkcja może zakończyć się przedwcześnie z powodu błędu wewnętrznego i domyślnie funkcja może zostać automatycznie ponowiona lub nie.

Bardziej typowo funkcja sterowana zdarzeniami może zakończyć się niepowodzeniem z powodu błędów zgłoszonych w samym kodzie funkcji. Oto niektóre z przyczyn tego zjawiska:

  • Funkcja zawiera błąd, a środowisko wykonawcze zgłasza wyjątek.
  • Funkcja nie może nawiązać połączenia z punktem końcowym usługi lub przekroczyć limit czasu podczas próby uzyskania dostępu do punktu końcowego.
  • Funkcja celowo zgłasza wyjątek (na przykład, gdy walidacja parametru nie powiedzie się).
  • Gdy funkcje napisane w Node.js zwracają odrzuconą obietnicę lub przekazują null wartość do wywołania zwrotnego.

W każdym z powyższych przypadków funkcja domyślnie przestaje działać, a zdarzenie jest odrzucane. Jeśli chcesz ponowić próbę wykonania funkcji po wystąpieniu błędu, możesz zmienić domyślną zasadę ponawiania, ustawiając właściwość „ponów próbę w przypadku niepowodzenia” . Powoduje to wielokrotne ponawianie próby zdarzenia przez maksymalnie wiele dni, aż do pomyślnego zakończenia funkcji.

Włączanie i wyłączanie ponownych prób

Korzystanie z konsoli GCP

Ponowne próby możesz włączyć lub wyłączyć w konsoli GCP w następujący sposób:

  1. Przejdź do strony Przegląd funkcji Cloud w konsoli Cloud Platform.

  2. Kliknij Utwórz funkcję . Możesz też kliknąć istniejącą funkcję, aby przejść do jej strony szczegółów i kliknąć Edytuj .

  3. Wypełnij wymagane pola dla swojej funkcji.

  4. Upewnij się, że pole Wyzwalacz jest ustawione na typ wyzwalacza oparty na zdarzeniach, taki jak Cloud Pub/Sub lub Cloud Storage.

  5. Rozwiń ustawienia zaawansowane, klikając Więcej .

  6. Zaznacz lub usuń zaznaczenie pola oznaczonego Spróbuj ponownie w przypadku niepowodzenia .

W kodzie funkcji

Dzięki Cloud Functions dla Firebase możesz włączyć ponawianie prób w kodzie funkcji. Aby to zrobić dla funkcji działającej w tle, takiej jak functions.foo.onBar(myHandler); , użyj runWith i skonfiguruj politykę niepowodzeń:

functions.runWith({failurePolicy: true}).foo.onBar(myHandler);

Ustawienie true jak pokazano, konfiguruje funkcję do ponawiania próby w przypadku niepowodzenia.

Najlepsze praktyki

W tej sekcji opisano najlepsze rozwiązania dotyczące korzystania z ponownych prób.

Użyj ponawiania, aby obsłużyć przejściowe błędy

Ponieważ funkcja jest ponawiana w sposób ciągły aż do pomyślnego wykonania, trwałe błędy, takie jak błędy, powinny zostać wyeliminowane z kodu poprzez testowanie przed włączeniem ponownych prób. Ponowne próby najlepiej służą do obsługi sporadycznych/przejściowych awarii, które mają wysokie prawdopodobieństwo rozwiązania po ponownych próbach, takich jak niestabilny punkt końcowy usługi lub przekroczenie limitu czasu.

Ustaw warunek końcowy, aby uniknąć nieskończonych pętli ponawiania

Najlepszym rozwiązaniem jest ochrona funkcji przed ciągłym zapętleniem podczas korzystania z ponownych prób. Możesz to zrobić, dołączając dobrze zdefiniowany warunek końcowy, zanim funkcja rozpocznie przetwarzanie. Zwróć uwagę, że ta technika działa tylko wtedy, gdy twoja funkcja uruchamia się pomyślnie i jest w stanie ocenić warunek końcowy.

Prostym, ale skutecznym podejściem jest odrzucanie zdarzeń z sygnaturami czasowymi starszymi niż określona godzina. Pomaga to uniknąć nadmiernego wykonywania, gdy awarie są trwałe lub trwają dłużej niż oczekiwano.

Na przykład ten fragment kodu odrzuca wszystkie zdarzenia starsze niż 10 sekund:

const eventAgeMs = Date.now() - Date.parse(event.timestamp);
const eventMaxAgeMs = 10000;
if (eventAgeMs > eventMaxAgeMs) {
  console.log(`Dropping event ${event} with age[ms]: ${eventAgeMs}`);
  callback();
  return;
}

Użyj catch z obietnicami

Jeśli funkcja ma włączone ponawianie prób, każdy nieobsługiwany błąd spowoduje ponowną próbę. Upewnij się, że kod przechwytuje wszelkie błędy, które nie powinny skutkować ponowną próbą.

Oto przykład tego, co powinieneś zrobić:

return doFooAsync().catch((err) => {
    if (isFatal(err)) {
        console.error(`Fatal error ${err}`);
    }
    return Promise.reject(err);
});

Spraw, aby funkcje sterowane zdarzeniami, które można ponawiać, były idempotentne

Funkcje sterowane zdarzeniami, które można ponowić, muszą być idempotentne. Oto kilka ogólnych wskazówek dotyczących tworzenia idempotentnej funkcji:

  • Wiele zewnętrznych interfejsów API (takich jak Stripe) umożliwia podanie klucza idempotentności jako parametru. Jeśli używasz takiego interfejsu API, powinieneś użyć identyfikatora zdarzenia jako klucza idempotentności.
  • Idempotentność działa dobrze w przypadku dostarczania co najmniej raz, ponieważ umożliwia bezpieczne ponowną próbę. Tak więc ogólną najlepszą praktyką przy pisaniu niezawodnego kodu jest połączenie idempotentności z ponawianiem prób.
  • Upewnij się, że Twój kod jest wewnętrznie idempotentny. Na przykład:
    • Upewnij się, że mutacje mogą wystąpić więcej niż raz bez zmiany wyniku.
    • Zapytanie o stan bazy danych w transakcji przed zmutowaniem stanu.
    • Upewnij się, że wszystkie skutki uboczne są same w sobie idempotentne.
  • Nałóż kontrolę transakcyjną poza funkcją, niezależnie od kodu. Na przykład utrwalaj stan gdzieś rejestrując, że dany identyfikator zdarzenia został już przetworzony.
  • Zajmij się zduplikowanymi wywołaniami funkcji poza pasmem. Na przykład masz osobny proces czyszczenia, który czyści po zduplikowanych wywołaniach funkcji.