Prostota Cloud Functions pozwala szybko opracowywać kod i uruchamiać go w środowisku bez serwera. W umiarkowanym zakresie koszt uruchamiania funkcji jest niski, a optymalizacja kodu może nie wydawać się priorytetem. Jednak wraz ze zwiększaniem skali wdrożenia optymalizacja kodu staje się coraz ważniejsza.
W tym dokumencie opisano, jak zoptymalizować sieć pod kątem funkcji. Oto kilka korzyści płynących z optymalizacji sieci:
- Skrócenie czasu procesora poświęcanego na nawiązywanie nowych połączeń wychodzących w przypadku każdego wywołania funkcji.
- Zmniejsz prawdopodobieństwo wyczerpania limitu połączeń lub limitów DNS.
Utrzymywanie trwałych połączeń
W tej sekcji znajdziesz przykłady utrzymywania trwałych połączeń w funkcji. Jeśli tego nie zrobisz, szybko wyczerpiesz limit połączeń.
W tej sekcji omówiono te scenariusze:
- HTTP/S
- Interfejsy API Google
Żądania HTTP/S
Poniżej podany jest zoptymalizowany fragment kodu, który pokazuje, jak utrzymywać trwałe połączenia zamiast tworzyć nowe połączenia 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.
Funkcja ta przyjmuje obiekt żądania (flask.Request
) i zwraca tekst odpowiedzi lub dowolny zbiór wartości, które można przekształcić w obiekt Response
za pomocą funkcji make_response
.
Dostęp do interfejsów API Google
Przykład poniżej korzysta z 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 jedno połączenie i 2 zapytania DNS na wywołanie. Aby uniknąć niepotrzebnych połączeń i zapytań DNS, utwórz obiekt klienta Pub/Sub w zakresie globalnym, jak pokazano w przykładzie poniżej:
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 korzysta z pamięci podręcznej w przypadku instancji biblioteki klienta, aby zmniejszyć liczbę połączeń wymaganych na wywołanie funkcji.
Funkcja ta przyjmuje obiekt żądania (flask.Request
) i zwraca tekst odpowiedzi lub dowolny zbiór wartości, które można przekształcić w obiekt Response
za pomocą funkcji make_response
.
Zmienna środowiskowa GCP_PROJECT
jest ustawiana automatycznie w środowisku wykonawczym Pythona 3.7. W późniejszych wersjach runtime’a pamiętaj, aby podać go podczas wdrażania funkcji. Zapoznaj się z artykułem Konfigurowanie zmiennych środowiskowych.
Połączenia wychodzące
Limity czasu żądań wychodzących
W przypadku żądań wysyłanych przez Twoją funkcję do sieci VPC występuje limit czasu po 10 minutach bezczynności. W przypadku żądań wysyłanych przez Twoją funkcję do internetu występuje limit czasu po 20 minutach bezczynności.
Resetowanie połączeń wychodzących
Strumienie połączeń z funkcją do sieci VPC i do internetu mogą być od czasu do czasu zamykane i zastępowane, gdy podstawowa infrastruktura zostanie ponownie uruchomiona lub zaktualizowana. Jeśli aplikacja używa wielokrotnie połączeń długotrwałych, zalecamy skonfigurowanie jej tak, aby ponownie nawiązywała połączenia, aby uniknąć ponownego korzystania z nieaktywnych połączeń.
Testowanie obciążenia funkcji
Aby zmierzyć, ile połączeń tworzy funkcja w średniej, po prostu wdróż ją jako funkcję HTTP i używ do jej wywołania określonej platformy do testowania wydajności. Jednym z możliwych wyborów jest Artillery, którą możesz wywołać za pomocą pojedynczego 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 Cloud Functions stronie limitu interfejsu API w Cloud Console. Jeśli wykorzystanie jest stale na poziomie około 30 (lub wielokrotności tej wartości), oznacza to, że na każde wywołanie nawiązujesz 1 (lub więcej) połączenie. Po optymalizacji kodu zobaczysz kilka (10–30) połączeń tylko na początku testu.
Na tej samej stronie możesz też porównać koszt procesora przed i po optymalizacji na wykresie dotyczącej limitu procesora.