Optymalizacja sieci

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

Z tego dokumentu dowiesz się, jak zoptymalizować sieć pod kątem swoich funkcji. Oto niektóre zalety optymalizacji sieci:

  • Skrócenie czasu pracy procesora poświęcanego na nawiązywanie nowych połączeń przy każdym wywołaniu funkcji.
  • Zmniejsz prawdopodobieństwo wyczerpywania się połączenia 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, mogą szybko wyczerpać limity połączeń.

W tej sekcji omawiamy następujące scenariusze:

  • HTTP/S
  • interfejsów API Google,

Żądania HTTP/S

Poniższy zoptymalizowany fragment kodu pokazuje, jak utrzymać trwał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 wykorzystuje pulę połączeń do wysyłania żądań HTTP. Zwraca on 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ą make_response.

Dostęp do interfejsów API Google

Poniższy przykład używa Cloud Pub/Sub, ale to podejście sprawdza się również w przypadku innych bibliotek klienta, takich jak Cloud Natural Language lub Cloud Spanner. Pamiętaj, że poprawa wydajności może zależeć od bieżącej implementacji konkretnych bibliotek klienckich.

Utworzenie obiektu klienckiego Pub/Sub skutkuje 1 połączeniem i 2 zapytaniami DNS na wywołanie. Aby uniknąć zbędnych połączeń i zapytań DNS, utwórz obiekt klienta Pub/Sub w zakresie globalnym, jak 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 wykorzystuje instancję biblioteki klienta z pamięci podręcznej, aby zmniejszyć liczbę połączeń wymaganych na każde wywołanie funkcji. Zwraca on 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ą make_response.

Zmienna środowiskowa GCP_PROJECT jest ustawiana automatycznie w środowisku wykonawczym Pythona 3.7. W późniejszych środowiskach wykonawczych pamiętaj, aby określić je przy wdrażaniu funkcji. Więcej informacji znajdziesz w artykule Konfigurowanie zmiennych środowiskowych.

Testowanie funkcji pod kątem obciążenia

Aby zmierzyć średnią liczbę połączeń wykonywanych przez Twoją funkcję, po prostu wdróż ją jako funkcję HTTP i użyj platformy do testowania wydajności, aby wywoływać ją przy określonych zapytaniach na sekundę. Jedną z dostępnych opcji jest Artillery, którą możesz wywołać z jednego wiersza:

$ artillery quick -d 300 -r 30 URL

To polecenie pobiera dany adres URL z szybkością 30 zapytań na sekundę przez 300 sekund.

Po zakończeniu testu sprawdź wykorzystanie limitu połączeń na stronie limitów interfejsu Cloud Functions API w Cloud Console. Jeśli użycie utrzymuje się regularnie około 30 (lub jest wielokrotnością poziomu wykorzystania), w każdym wywołaniu tworzysz jedno (lub kilka) połączeń. Gdy zoptymalizujesz kod, na początku testu powinno pojawić się kilka (10–30) połączeń.

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