Функции и управляемые среды выполнения

Что это такое

Функция — это код с объявленной точкой входа, настройкой памяти и тайм-аутом, загруженный на платформу, которая запускает его по требованию. Машину вы не выбираете: вы выбираете среду выполнения и размер, а платформа решает, где это выполнится. Настоящая единица проектирования — не функция, а сборка: функция, запускаемая событием, обращается к управляемой базе данных, пишет в управляемое хранилище и отправляет результат в очередь.

Почему это важно

Взгляд на бессерверную модель как на «серверы поменьше» ведёт к неверным решениям: функция ведёт себя как долгоживущий процесс, хранит состояние в памяти и предполагает, что следующий запрос обслужит тот же экземпляр. Как только модель платформы понята, те же ограничения становятся руководством к проектированию: состояние уходит в управляемое хранилище, работа разбивается на шаги, каждый из которых быстро завершается, а отказ считается ожидаемым и повторяется. Именно эта смена рамки и есть настоящий навык, и она переносится на среду выполнения любого провайдера.

Ключевые идеи

Единица развёртывания — функция, единица проектирования — событие

Функция выполняется потому, что что-то произошло: HTTP-запрос, сообщение в очереди, появившийся в хранилище файл, расписание. Событие и есть интерфейс, и оно определяет форму обработчика и поведение при повторных попытках. Поэтому проектировать событие важнее, чем писать обработчик.

объект создан в хранилище -> функция -> изменить размер -> записать миниатюру
сообщение из очереди      -> функция -> отправить письмо
расписание                -> функция -> свернуть метрики за вчера

Каждый вызов где-то стартует холодным

Платформа может переиспользовать тёплую среду выполнения между запросами, но не обещает этого. Состояние, оставленное в памяти, может там оказаться в следующий раз, а может и нет; инициализация может стоить десятки миллисекунд или несколько секунд. Ответ на это — делать инициализацию дешёвой, выносить переиспользуемые соединения за пределы обработчика там, где среда это позволяет, и принять холодные старты как свойство задержки этой модели, а не как ошибку.

Тайм-ауты и память задают рамки проектирования

У функции есть предельное время работы и потолок памяти, и эти величины связаны: в некоторых платформах процессор масштабируется вместе с памятью, поэтому вычислительно тяжёлый обработчик можно починить, дав ему больше памяти. Работе, которая должна идти минутами или часами, в функции не место — её место в задании или в контейнере. Узнавать об этом из тайм-аута в продакшене дорого.

Управляемые сервисы — вторая половина

Функция без состояния полезна только рядом с управляемой базой данных, очередью, хранилищем и провайдером идентичности. Модель идентичности провайдера позволяет функции принять роль и добраться до этих сервисов без учётных данных в коде. Чем большая часть сборки управляемая, тем меньше нужно эксплуатировать — в этом и смысл.

Платите за вызов и за длительность

Счёт идёт за запрос и за гигабайт-секунду, поэтому стоимость тесно следует за фактическим использованием. Для прерывистой работы это настоящее преимущество, а для ровной высокой нагрузки — недостаток: зарезервированная ёмкость или контейнер дешевле за единицу. Правильное сравнение — не «бессерверное против серверов», а две цены при вашей ожидаемой нагрузке.

Практика

Превратите один эндпоинт в функцию

Возьмите небольшой HTTP-эндпоинт, который вы знаете, и опишите его как функцию: что её запускает, какое состояние она читает, какой тайм-аут ей нужен и что она записывает в ответ.

Готово, когда: вы можете назвать выбранные тайм-аут и память и указать одну вещь из оригинала, которая не переживёт переноса.

Разбейте длинную задачу на шаги

Возьмите задачу, которая занимает несколько минут, и разделите её на цепочку вызовов, каждый из которых укладывается в секунды, передавая прогресс через очередь или хранилище.

Готово, когда: вход и выход каждого шага — именованное событие или объект, и ни один шаг не зависит от памяти предыдущего.

Частые ошибки

  • Хранить состояние между вызовами — следующий запрос может попасть на свежую среду выполнения и ничего не найти; исправить: держите состояние в управляемом хранилище.
  • Создавать соединение при каждом вызове — задержка и текучка соединений растут вместе с нагрузкой; исправить: переиспользуйте соединения за пределами обработчика.
  • Рассчитывать на долгоживущий процесс — тайм-аут приходит посреди работы, и платформа повторяет её; исправить: разбейте на шаги, каждый из которых быстро завершается.
  • Зашивать учётные данные в код — их должна выдавать идентичность функции; исправить: дайте функции роль и получайте токены во время выполнения.
  • Выбирать бессерверную модель для ровной высокой нагрузки — цена за вызов не самая дешёвая форма; исправить: сравните цену при ожидаемой нагрузке, прежде чем брать обязательства.

Что читать дальше