Prostota Cloud Functions pozwala szybko tworzyć 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, na przykład Cloud Natural Language lub 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 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ć czasami zamykane i zastępowane, gdy podstawowa infrastruktura jest restartowana lub aktualizowana. 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 używania nieaktywnego połączenia.
Testowanie obciążenia funkcji
Aby zmierzyć, ile połączeń na ogół wykonuje Twoja funkcja, po prostu wdróż ją jako funkcję HTTP i używ do wywołania jej w określonym QPS framework 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 stronie limitu interfejsu API w Cloud Console.Cloud Functions 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.