Optymalizacja sieci

Prostota Cloud Functions pozwala szybko tworzyć kod i uruchamiać go w środowisku bezserwerowym. Przy umiarkowanej skali koszty uruchamiania funkcji są niskie, więc optymalizacja kodu może nie wydawać się priorytetem. Wraz ze wzrostem skali wdrożenia optymalizacja kodu staje się coraz ważniejsza.

Z tego dokumentu dowiesz się, jak zoptymalizować sieć na potrzeby funkcji. Oto niektóre korzyści z optymalizacji sieci:

Utrzymywanie stałych połączeń

W tej sekcji znajdziesz przykłady utrzymywania stałych połączeń w funkcji. Jeśli tego nie zrobisz, możesz szybko wyczerpać limity połączeń.

W tej sekcji opisujemy te scenariusze:

  • HTTP/S
  • Interfejsy API Google

Żądania HTTP/S

Zoptymalizowany fragment kodu poniżej pokazuje, jak utrzymywać stałe połączenia zamiast tworzyć nowe połączenie 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ą funkcji make_response.

Dostęp do interfejsów API Google

W przykładzie poniżej użyto Cloud Pub/Sub, ale to podejście działa też w przypadku innych bibliotek klienta, np. Cloud Natural Language lub Cloud Spanner. Pamiętaj, że poprawa skuteczności może zależeć od bieżącej implementacji konkretnych bibliotek klienta.

Utworzenie obiektu klienta Pub/Sub powoduje nawiązanie 1 połączenia i wykonanie 2 zapytań DNS przy każdym wywołaniu. 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 w pamięci podręcznej, aby zmniejszyć liczbę połączeń wymaganych przy każdym wywołaniu 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ą funkcji make_response.

Zmienna środowiskowa GCP_PROJECT jest ustawiana automatycznie w środowisku wykonawczym Python 3.7. W późniejszych środowiskach wykonawczych pamiętaj, aby określić go podczas wdrażania funkcji. Zobacz Konfigurowanie zmiennych środowiskowych.

Połączenia wychodzące

Przekroczenie limitu czasu żądania wychodzącego

W przypadku żądań z funkcji do sieci VPC po 10 minutach bezczynności następuje przekroczenie limitu czasu. W przypadku żądań z funkcji do internetu po 20 minutach bezczynności następuje przekroczenie limitu czasu.

Resetowanie połączeń wychodzących

Strumienie połączeń z funkcji do sieci VPC i internetu mogą być sporadycznie zamykane i zastępowane, gdy podstawowa infrastruktura jest ponownie uruchamiana lub aktualizowana. Jeśli aplikacja ponownie wykorzystuje długotrwałe połączenia, zalecamy skonfigurowanie jej tak, aby ponownie nawiązywała połączenia, co pozwoli uniknąć ponownego wykorzystania martwego połączenia.

Testowanie obciążenia funkcji

Aby zmierzyć średnią liczbę połączeń wykonywanych przez funkcję, wdróż ją jako funkcję HTTP i użyj platformy do testowania wydajności, aby wywoływać ją z określoną liczbą zapytań na sekundę. Jedną z możliwych opcji jest Artillery, którą możesz 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 limitu interfejsu API w Cloud Console.Cloud Functions Jeśli wykorzystanie jest stale na poziomie około 30 (lub jego wielokrotności), oznacza to, że przy każdym wywołaniu nawiązujesz jedno (lub kilka) połączeń. Po zoptymalizowaniu kodu na początku testu powinno wystąpić tylko kilka (10–30) połączeń.

Na tej samej stronie możesz też porównać koszt procesora przed optymalizacją i po niej na wykresie limitu procesora.