Como otimizar as redes

A simplicidade do Cloud Functions permite desenvolver um código rapidamente e executá-lo em um ambiente sem servidor. Em escala moderada, o custo de executar funções é baixo, e otimizar seu código pode não parecer uma prioridade tão urgente. No entanto, à medida que a implantação evolui, otimizar o código torna-se cada vez mais importante.

Neste documento, você verá como otimizar a rede para suas funções. Veja a seguir alguns dos benefícios da otimização de rede:

  • Reduz o tempo de CPU gasto para estabelecer novas conexões de saída em cada chamada de função.
  • Reduz a probabilidade de ficar sem conexão ou cotas de DNS.

Como manter conexões permanentes

Nesta seção, você verá exemplos sobre como manter conexões permanentes em uma função. As cotas de conexão poderão se esgotar rapidamente caso você não faça isso.

As seguintes situações são abordadas nesta seção:

  • HTTP/S
  • APIs do Google

Solicitações HTTP/S

Veja abaixo no snippet de código otimizado como manter conexões permanentes em vez de criar uma nova conexão a cada invocação de função:

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

Essa função HTTP usa um pool de conexões para fazer solicitações HTTP. Ela recebe um objeto de solicitação (flask.Request) e retorna o texto de resposta ou qualquer conjunto de valores que possa ser transformado em um objeto Response usando make_response.

Como acessar APIs do Google

O Cloud Pub/Sub foi usado neste exemplo, mas esta abordagem também funciona em outras bibliotecas de cliente, como a Cloud Natural Language ou a Cloud Spanner. As melhorias no desempenho podem depender da implementação atual de algumas bibliotecas de cliente.

Criar um objeto de cliente PubSub resulta em uma conexão e duas consultas DNS por invocação. Para evitar conexões e consultas DNS desnecessárias, crie o objeto de cliente PubSub no escopo global, conforme mostrado no exemplo a seguir:

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

Essa função HTTP usa uma instância de biblioteca de cliente armazenada em cache para reduzir o número de conexões necessárias por invocação de função. Ela recebe um objeto de solicitação (flask.Request) e retorna o texto de resposta ou qualquer conjunto de valores que possa ser transformado em um objeto Response usando make_response.

A variável de ambiente GCP_PROJECT é definida automaticamente no ambiente de execução do Python 3.7. Em ambientes de execução mais recentes, especifique-a durante a implantação da função. Consulte Configurar variáveis de ambiente.

Conexões de saída

Tempo limite das solicitações de saída

Após 10 minutos de inatividade, há um tempo limite para solicitações da sua função para a rede VPC. Para solicitações da sua função para a Internet, há um tempo limite após 20 minutos de inatividade.

Redefinições de conexão de saída

Os streams de conexão da função para a rede VPC e a Internet podem ser encerrados ocasionalmente e substituídos quando a infraestrutura subjacente for reiniciada ou atualizada. Se o aplicativo reutilizar conexões de longa duração, recomendamos configurá-lo para restabelecer as conexões e evitar a reutilização de uma conexão inativa.

Como testar a carga da sua função

Para analisar quantas conexões a função realiza em média, basta fazer a implantação dela como uma função HTTP e usar uma biblioteca de testes de desempenho para invocá-la em determinada taxa de QPS. Uma opção possível é o Artillery, que pode ser invocado com uma única linha:

$ artillery quick -d 300 -r 30 URL

Este comando busca o URL fornecido em 30 QPS durante 300 segundos.

Depois de realizar o teste, verifique o uso da cota de conexão na página de cotas da API de Cloud Functions no console do Cloud. Se o uso for consistente em torno de 30 (ou múltiplos de 30), você está estabelecendo uma (ou várias) conexões a cada invocação. Depois de otimizar seu código, você verá algumas conexões (10 a 30) ocorrerem apenas no início do teste.

Nessa mesma página, você também pode comparar o custo de CPU antes e depois da otimização no gráfico de cota.