Простота Cloud Functions позволяет быстро разрабатывать код и запускать его в бессерверной среде. При умеренном масштабе стоимость выполнения функций низкая, и оптимизация кода может не казаться приоритетной задачей. Однако по мере масштабирования развертывания оптимизация кода становится все более важной.
В этом документе описывается, как оптимизировать сеть для ваших функций. Вот некоторые из преимуществ оптимизации сети:
- Сократите процессорное время, которое тратится на установку новых исходящих подключений при каждом вызове функции.
- Снижается вероятность исчерпания квот на подключения или DNS.
Поддержание постоянных подключений
В этом разделе приведены примеры того, как поддерживать постоянные подключения в функции. В противном случае квоты на подключения могут быть быстро исчерпаны.
В этом разделе рассматриваются следующие сценарии:
- HTTPS
- API Google
HTTP/S-запросы
В оптимизированном фрагменте кода ниже показано, как поддерживать постоянные подключения, а не создавать новое подключение при каждом вызове функции:
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!")
Эта функция HTTP использует пул подключений для выполнения HTTP-запросов. Она принимает объект запроса (flask.Request) и возвращает текст ответа или любой набор значений, который можно преобразовать в объект Response с помощью make_response.
Доступ к API Google
В примере ниже используется Cloud Pub/Sub, но этот подход также работает для других клиентских библиотек, например Cloud Natural Language или Cloud Spanner. Обратите внимание, что повышение эффективности может зависеть от текущей реализации определенных клиентских библиотек.
При создании клиентского объекта издатель-подписчик выполняется одно подключение и два DNS-запроса на вызов. Чтобы избежать ненужных подключений и DNS-запросов, создайте клиентский объект издатель-подписчик в глобальной области видимости, как показано в примере ниже.
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")
Эта HTTP-функция использует кешированный экземпляр клиентской библиотеки, чтобы уменьшить количество подключений, необходимых для каждого вызова функции. Она принимает объект запроса (flask.Request) и возвращает текст ответа или любой набор значений, который можно преобразовать в объект Response с помощью make_response.
Переменная среды GCP_PROJECT задается автоматически в среде выполнения Python 3.7. В более поздних версиях среды выполнения обязательно укажите его при развертывании функции. Подробнее о настройке переменных среды…
Исходящие подключения
Тайм-ауты исходящих запросов
Если в течение 10 минут не поступает запросов от функции к сети VPC, время ожидания истекает. Для запросов из функции в интернет время ожидания составляет 20 минут.
Сбросы исходящих подключений
Потоки подключения от функции к сети VPC и интернету могут периодически прерываться и заменяться при перезапуске или обновлении базовой инфраструктуры. Если ваше приложение повторно использует долгосрочные подключения, рекомендуем настроить его так, чтобы оно заново устанавливало подключения. Это позволит избежать повторного использования неработающего подключения.
Нагрузочное тестирование функции
Чтобы узнать, сколько подключений в среднем выполняет функция, разверните ее как функцию HTTP и используйте фреймворк для тестирования производительности, чтобы вызвать ее при определенном количестве запросов в секунду. Один из возможных вариантов – Artillery. Его можно вызвать одной строкой:
$ artillery quick -d 300 -r 30 URL
Эта команда извлекает указанный URL-адрес со скоростью 30 запросов в секунду в течение 300 секунд.
После тестирования проверьте использование квоты на подключения на странице квоты API Cloud Functions в консоли Google Cloud. Если использование постоянно составляет около 30 (или кратно этому числу), вы устанавливаете одно (или несколько) подключений при каждом вызове. После оптимизации кода вы должны увидеть, что в начале теста происходит лишь несколько (10–30) подключений.
На этой же странице можно сравнить стоимость ЦП до и после оптимизации на графике квоты ЦП.