Optimización de redes

La simplicidad de Cloud Functions le permite desarrollar código rápidamente y ejecutarlo en un entorno sin servidor. A escala moderada, el costo de ejecutar funciones es bajo y optimizar su código puede no parecer una alta prioridad. Sin embargo, a medida que su implementación crece, optimizar su código se vuelve cada vez más importante.

Este documento describe cómo optimizar la red para sus funciones. Algunos de los beneficios de optimizar el networking son los siguientes:

  • Reduzca el tiempo de CPU dedicado a establecer nuevas conexiones en cada llamada de función.
  • Reduzca la probabilidad de quedarse sin conexión o cuotas de DNS.

Mantener conexiones persistentes

Esta sección ofrece ejemplos de cómo mantener conexiones persistentes en una función. De lo contrario, las cuotas de conexión se pueden agotar rápidamente.

En esta sección se tratan los siguientes escenarios:

  • HTTP/S
  • API de Google

Solicitudes HTTP/S

El siguiente fragmento de código optimizado muestra cómo mantener conexiones persistentes en lugar de crear una nueva conexión en cada invocación de función:

Nodo.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();
});

Pitón

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!")
    

Esta función HTTP utiliza un grupo de conexiones para realizar solicitudes HTTP. Toma un objeto de solicitud ( flask.Request ) y devuelve el texto de respuesta, o cualquier conjunto de valores que se pueda convertir en un objeto Response usando make_response .

Accediendo a las API de Google

El siguiente ejemplo utiliza Cloud Pub/Sub , pero este enfoque también funciona para otras bibliotecas cliente, por ejemplo, Cloud Natural Language o Cloud Spanner . Tenga en cuenta que las mejoras de rendimiento pueden depender de la implementación actual de bibliotecas cliente particulares.

La creación de un objeto de cliente Pub/Sub genera una conexión y dos consultas DNS por invocación. Para evitar conexiones innecesarias y consultas de DNS, cree el objeto de cliente Pub/Sub en el ámbito global como se muestra en el siguiente ejemplo:

nodo.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');
        }
    });
});

Pitón

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")
    

Esta función HTTP utiliza una instancia de biblioteca cliente almacenada en caché para reducir la cantidad de conexiones necesarias por invocación de función. Toma un objeto de solicitud ( flask.Request ) y devuelve el texto de respuesta, o cualquier conjunto de valores que se pueda convertir en un objeto Response usando make_response .

La variable de entorno GCP_PROJECT se configura automáticamente en el tiempo de ejecución de Python 3.7. En tiempos de ejecución posteriores, asegúrese de especificarlo en la implementación de la función. Consulte Configurar variables de entorno .

Prueba de carga de su función

Para medir cuántas conexiones realiza su función en promedio, simplemente impleméntela como una función HTTP y use un marco de prueba de rendimiento para invocarla en ciertos QPS. Una posible opción es Artillería , que puedes invocar con una sola línea:

$ artillery quick -d 300 -r 30 URL

Este comando recupera la URL proporcionada a 30 QPS durante 300 segundos.

Después de realizar la prueba, verifique el uso de su cuota de conexión en la página de cuota de API de Cloud Functions en Cloud Console. Si el uso es consistentemente alrededor de 30 (o su múltiplo), estás estableciendo una (o varias) conexiones en cada invocación. Después de optimizar su código, debería ver que se producen algunas conexiones (10-30) solo al comienzo de la prueba.

También puede comparar el costo de la CPU antes y después de la optimización en el gráfico de cuota de CPU en la misma página.