W tym dokumencie opisano najlepsze praktyki dotyczące projektowania, wdrażania, testowania i wdrażania Cloud Functions.
Poprawność
W tej sekcji opisano ogólne najlepsze praktyki dotyczące projektowania i wdrażania Cloud Functions.
Napisz funkcje idempotentne
Twoje funkcje powinny dawać ten sam wynik, nawet jeśli są wywoływane wiele razy. Dzięki temu możesz ponowić próbę wywołania, jeśli poprzednie wywołanie nie powiedzie się w trakcie wykonywania kodu. Aby uzyskać więcej informacji, zobacz ponawianie prób funkcji sterowanych zdarzeniami .
Nie rozpoczynaj działań w tle
Aktywność w tle to wszystko, co dzieje się po zakończeniu funkcji. Wywołanie funkcji kończy się, gdy funkcja zwróci lub w inny sposób zasygnalizuje zakończenie, na przykład poprzez wywołanie argumentu callback
w funkcjach sterowanych zdarzeniami Node.js. Jakikolwiek kod uruchomiony po łagodnym zakończeniu nie może uzyskać dostępu do procesora i nie spowoduje żadnego postępu.
Ponadto, gdy w tym samym środowisku wykonywane jest kolejne wywołanie, działanie w tle zostaje wznowione, zakłócając nowe wywołanie. Może to prowadzić do nieoczekiwanych zachowań i błędów trudnych do zdiagnozowania. Dostęp do sieci po zakończeniu działania funkcji zwykle prowadzi do zresetowania połączeń (kod błędu ECONNRESET
).
Aktywność w tle często można wykryć w dziennikach poszczególnych wywołań, znajdując wszystko, co jest zarejestrowane po wierszu informującym o zakończeniu wywołania. Aktywność w tle może czasami być ukryta głębiej w kodzie, zwłaszcza gdy obecne są operacje asynchroniczne, takie jak wywołania zwrotne lub liczniki czasu. Przejrzyj swój kod, aby upewnić się, że wszystkie operacje asynchroniczne zostały zakończone przed zakończeniem funkcji.
Zawsze usuwaj pliki tymczasowe
Lokalna pamięć dyskowa w katalogu tymczasowym to system plików w pamięci. Zapisywane pliki zużywają pamięć dostępną dla Twojej funkcji i czasami utrzymują się pomiędzy wywołaniami. Nieusunięcie tych plików może ostatecznie doprowadzić do błędu braku pamięci i późniejszego zimnego startu.
Możesz zobaczyć ilość pamięci wykorzystywanej przez daną funkcję wybierając ją na liście funkcji w Konsoli GCP i wybierając Wykres zużycia pamięci .
Nie próbuj pisać poza katalogiem tymczasowym i pamiętaj, aby do tworzenia ścieżek plików używać metod niezależnych od platformy/systemu operacyjnego.
Możesz zmniejszyć wymagania dotyczące pamięci podczas przetwarzania większych plików za pomocą potokowania. Na przykład możesz przetworzyć plik w Cloud Storage, tworząc strumień odczytu, przepuszczając go przez proces oparty na strumieniu i zapisując strumień wyjściowy bezpośrednio w Cloud Storage.
Ramy funkcji
Podczas wdrażania funkcji struktura funkcji jest automatycznie dodawana jako zależność przy użyciu jej bieżącej wersji. Aby mieć pewność, że te same zależności są spójnie instalowane w różnych środowiskach, zalecamy przypięcie funkcji do określonej wersji Framework funkcji.
Aby to zrobić, umieść preferowaną wersję w odpowiednim pliku blokady (na przykład package-lock.json
dla Node.js lub requirements.txt
dla Pythona).
Narzędzia
W tej sekcji znajdują się wskazówki dotyczące używania narzędzi do wdrażania, testowania i interakcji z Cloud Functions.
Rozwój lokalny
Wdrażanie funkcji zajmuje trochę czasu, dlatego często szybsze jest przetestowanie kodu funkcji lokalnie.
Programiści Firebase mogą korzystać z emulatora funkcji chmurowych Firebase CLI .Użyj Sendgrid do wysyłania e-maili
Cloud Functions nie zezwala na połączenia wychodzące na porcie 25, dlatego nie można nawiązywać niezabezpieczonych połączeń z serwerem SMTP. Zalecanym sposobem wysyłania wiadomości e-mail jest użycie SendGrid . Inne opcje wysyłania wiadomości e-mail znajdziesz w samouczku Wysyłanie wiadomości e-mail z instancji dla Google Compute Engine.
Wydajność
W tej sekcji opisano najlepsze praktyki optymalizacji wydajności.
Używaj zależności mądrze
Ponieważ funkcje są bezstanowe, środowisko wykonawcze jest często inicjowane od zera (podczas tak zwanego zimnego startu ). Kiedy następuje zimny start, oceniany jest globalny kontekst funkcji.
Jeśli funkcje importują moduły, czas ładowania tych modułów może zwiększyć opóźnienie wywołania podczas zimnego startu. Możesz zmniejszyć to opóźnienie, a także czas potrzebny do wdrożenia funkcji, poprawnie ładując zależności i nie ładując zależności, których funkcja nie używa.
Użyj zmiennych globalnych, aby ponownie wykorzystać obiekty w przyszłych wywołaniach
Nie ma gwarancji, że stan Funkcji Cloud zostanie zachowany na potrzeby przyszłych wywołań. Jednak Cloud Functions często odtwarza środowisko wykonawcze poprzedniego wywołania. Jeśli zadeklarujesz zmienną w zasięgu globalnym, jej wartość będzie mogła zostać ponownie użyta w kolejnych wywołaniach bez konieczności ponownego obliczania.
W ten sposób możesz buforować obiekty, których odtworzenie przy każdym wywołaniu funkcji może być kosztowne. Przeniesienie takich obiektów z treści funkcji do zakresu globalnego może skutkować znaczną poprawą wydajności. Poniższy przykład tworzy ciężki obiekt tylko raz na instancję funkcji i udostępnia go wszystkim wywołaniom funkcji docierającym do danej instancji:
console.log('Global scope');
const perInstance = heavyComputation();
const functions = require('firebase-functions');
exports.function = functions.https.onRequest((req, res) => {
console.log('Function invocation');
const perFunction = lightweightComputation();
res.send(`Per instance: ${perInstance}, per function: ${perFunction}`);
});
Szczególnie ważne jest buforowanie połączeń sieciowych, odniesień do bibliotek i obiektów klientów API w zakresie globalnym. Przykłady można znaleźć w sekcji Optymalizacja sieci .
Wykonaj leniwą inicjalizację zmiennych globalnych
Jeśli zainicjujesz zmienne w zasięgu globalnym, kod inicjujący będzie zawsze wykonywany poprzez wywołanie zimnego startu, co zwiększy opóźnienie Twojej funkcji. W niektórych przypadkach powoduje to sporadyczne przekroczenia limitu czasu wywoływanych usług, jeśli nie są one odpowiednio obsługiwane w bloku try
/ catch
. Jeśli niektóre obiekty nie są używane we wszystkich ścieżkach kodu, rozważ leniwą inicjalizację ich na żądanie:
const functions = require('firebase-functions');
let myCostlyVariable;
exports.function = functions.https.onRequest((req, res) => {
doUsualWork();
if(unlikelyCondition()){
myCostlyVariable = myCostlyVariable || buildCostlyVariable();
}
res.status(200).send('OK');
});
Jest to szczególnie ważne, jeśli definiujesz kilka funkcji w jednym pliku, a różne funkcje korzystają z różnych zmiennych. Jeśli nie użyjesz leniwej inicjalizacji, możesz marnować zasoby na zmienne, które są inicjowane, ale nigdy nie są używane.
Ogranicz zimne starty, ustawiając minimalną liczbę instancji
Domyślnie Cloud Functions skaluje liczbę instancji na podstawie liczby przychodzących żądań. Możesz zmienić to domyślne zachowanie, ustawiając minimalną liczbę instancji, które Cloud Functions muszą utrzymywać w gotowości do obsługi żądań. Ustawienie minimalnej liczby instancji ogranicza zimne uruchamianie aplikacji. Jeśli aplikacja jest wrażliwa na opóźnienia, zalecamy ustawienie minimalnej liczby instancji.
Aby uzyskać więcej informacji na temat tych opcji środowiska wykonawczego, zobacz Kontrolowanie zachowania skalowania .Dodatkowe zasoby
Więcej informacji na temat optymalizacji wydajności można znaleźć w filmie „Google Cloud Performance Atlas” Funkcje chmury Czas zimnego rozruchu .