Prostota Cloud Functions pozwala szybko tworzyć kod i uruchamiać go w środowisku bezserwerowym. Przy umiarkowanej skali koszt uruchamiania funkcji jest niski, a optymalizacja kodu może nie wydawać się priorytetem. Jednak w miarę skalowania wdrożenia optymalizacja kodu staje się coraz ważniejsza.
Ten dokument opisuje, jak zoptymalizować sieć pod kątem funkcji. Oto niektóre korzyści płynące z optymalizacji sieci:
- skrócić czas pracy procesora poświęcany na nawiązywanie nowych połączeń wychodzących przy każdym wywołaniu funkcji;
- Zmniejsz prawdopodobieństwo wyczerpania limitów połączeń lub DNS.
Utrzymywanie stałych połączeń
W tej sekcji znajdziesz przykłady utrzymywania trwałych połączeń w funkcji. Jeśli tego nie zrobisz, limity połączeń mogą zostać szybko wyczerpane.
W tej sekcji opisano te scenariusze:
- HTTP/S
- Interfejsy API Google
Żądania HTTP/S
Poniższy zoptymalizowany fragment kodu pokazuje, jak utrzymywać stałe połączenia zamiast tworzyć nowe przy każdym wywołaniu funkcji:
Node.js
const http = require('http'); const functions = require('firebase-functions'); // Setting the `keepAlive` option to `true` keeps // connections open between function invocations const agent = new http.Agent({keepAlive: true}); exports.function = functions.https.onRequest((request, response) => { req = http.request({ host: '' , port: 80, path: '' , method: 'GET', agent: agent, // Holds the connection open after the first invocation }, res => { let rawData = ''; res.setEncoding('utf8'); res.on('data', chunk => { rawData += chunk; }); res.on('end', () => { response.status(200).send(`Data: ${rawData}`); }); }); req.on('error', e => { response.status(500).send(`Error: ${e.message}`); }); req.end(); });
Python
from firebase_functions import https_fn import requests # Create a global HTTP session (which provides connection pooling) session = requests.Session() @https_fn.on_request() def connection_pooling(request): # The URL to send the request to url = "http://example.com" # Process the request response = session.get(url) response.raise_for_status() return https_fn.Response("Success!")
Ta funkcja HTTP używa puli połączeń do wysyłania żądań HTTP. Przyjmuje obiekt żądania (flask.Request) i zwraca tekst odpowiedzi lub dowolny zestaw wartości, które można przekształcić w obiekt Response za pomocą make_response.
Uzyskiwanie dostępu do interfejsów API Google
W przykładzie poniżej używamy Cloud Pub/Sub, ale to podejście działa też w przypadku innych bibliotek klienta, takich jak Cloud Natural Language czy Cloud Spanner. Pamiętaj, że poprawa wydajności może zależeć od bieżącej implementacji poszczególnych bibliotek klienta.
Utworzenie obiektu klienta Pub/Sub powoduje nawiązanie 1 połączenia i wysłanie 2 zapytań DNS na wywołanie. Aby uniknąć niepotrzebnych połączeń i zapytań DNS, utwórz obiekt klienta Pub/Sub w zakresie globalnym, jak pokazano w poniższym przykładzie:
node.js
const PubSub = require('@google-cloud/pubsub'); const functions = require('firebase-functions'); const pubsub = PubSub(); exports.function = functions.https.onRequest((req, res) => { const topic = pubsub.topic('' ); topic.publish('Test message', err => { if (err) { res.status(500).send(`Error publishing the message: ${err}`); } else { res.status(200).send('1 message published'); } }); });
Python
import os from firebase_functions import https_fn from google.cloud import pubsub_v1 # from firebase_functions import https_fn # Create a global Pub/Sub client to avoid unneeded network activity pubsub = pubsub_v1.PublisherClient() @https_fn.on_request() def gcp_api_call(request): project = os.getenv("GCP_PROJECT") request_json = request.get_json() topic_name = request_json["topic"] topic_path = pubsub.topic_path(project, topic_name) # Process the request data = b"Test message" pubsub.publish(topic_path, data=data) return https_fn.Response("1 message published")
Ta funkcja HTTP używa instancji biblioteki klienta z pamięci podręcznej, aby zmniejszyć liczbę połączeń wymaganych na wywołanie funkcji. Przyjmuje obiekt żądania (flask.Request) i zwraca tekst odpowiedzi lub dowolny zestaw wartości, które można przekształcić w obiekt Response za pomocą make_response.
Zmienna środowiskowa GCP_PROJECT jest ustawiana automatycznie w środowisku wykonawczym Pythona 3.7. W nowszych środowiskach wykonawczych pamiętaj, aby określić je podczas wdrażania funkcji. Zobacz Konfigurowanie zmiennych środowiskowych.
Połączenia wychodzące
Przekroczenia limitu czasu żądań wychodzących
Żądania wysyłane z funkcji do sieci VPC są przerywane po 10 minutach bezczynności. W przypadku żądań wysyłanych z funkcji do internetu limit czasu jest osiągany po 20 minutach bezczynności.
Zresetowane połączenia wychodzące
Strumienie połączeń z funkcji do sieci VPC i internetu mogą być czasami przerywane i zastępowane, gdy infrastruktura bazowa jest ponownie uruchamiana lub aktualizowana. Jeśli aplikacja ponownie używa długotrwałych połączeń, zalecamy skonfigurowanie jej tak, aby ponownie nawiązywała połączenia, co pozwoli uniknąć ponownego użycia nieaktywnego połączenia.
Testowanie obciążenia funkcji
Aby zmierzyć, ile połączeń średnio wykonuje funkcja, wdróż ją jako funkcję HTTP i użyj platformy do testowania wydajności, aby wywołać ją przy określonej liczbie zapytań na sekundę. Jednym z możliwych wyborów jest Artillery, które można wywołać za pomocą jednego wiersza:
$ artillery quick -d 300 -r 30 URL
To polecenie pobiera podany adres URL z częstotliwością 30 zapytań na sekundę przez 300 sekund.
Po przeprowadzeniu testu sprawdź wykorzystanie limitu połączeń na stronie limitów interfejsu API Cloud Functions w konsoli Google Cloud. Jeśli wykorzystanie wynosi stale około 30 (lub wielokrotność tej liczby), oznacza to, że przy każdym wywołaniu nawiązujesz jedno (lub kilka) połączeń. Po zoptymalizowaniu kodu powinno być widocznych tylko kilka (10–30) połączeń na początku testu.
Możesz też porównać koszt procesora przed optymalizacją i po niej na wykresie limitu procesora na tej samej stronie.