Но что такое бессерверные вычисления на самом деле? По сути, это модель облачных вычислений, где разработчик фокусируется исключительно на создании бизнес-логики, не тратя время на настройку серверов, обновление ПО и мониторинг инфраструктуры. При таком подходе код запускается в изолированных контейнерах, которые создаются по запросу и существуют ровно столько, сколько необходимо для выполнения функции. AWS Lambda выступает ключевым игроком в бессерверных технологиях. Эта служба позволяет запускать код в ответ на события без необходимости управления базовой инфраструктурой. В сочетании с JavaScript и Node.js, Lambda становится мощным инструментом для создания API, способных обрабатывать тысячи запросов в секунду.
JavaScript, будучи одним из самых популярных языков программирования, хорошо подходит для бессерверной разработки. Его неблокирующая модель ввода-вывода и асинхронный характер делают его идеальным выбором для обработки множества параллельных запросов. Node.js, в свою очередь, обеспечивает легковесное и эффективное окружение выполнения JavaScript, что критично важно в контексте бессерверных функций, где каждая миллисекунда выполнения влияет на итоговую стоимость.
Преимущества использования AWS Lambda с JavaScript многочисленны. Во-первых, это автоматическое масштабирование — приложение моментально реагирует на входящий трафик, будь то десять запросов или десять миллионов. Во-вторых, это экономическая эффективность — платим только за то время, которое код реально выполняется, без необходимости оплачивать простаивающие серверы. Наконец, это скорость разработки — благодаря событийно-ориентированной архитектуре Lambda и неблокирующей модели Node.js, разработчики могут быстро создавать и развертывать API, концентрируясь на написании кода, а не на управлении инфраструктурой. Бессерверная архитектура решает множество задач, с которыми сталкиваются разработчики. Это и вопросы масштабирования при пиковых нагрузках, и проблемы оптимизации затрат в периоды низкой активности, и сложности с обновлением инфраструктуры. Вместо того, чтобы тратить ресурсы на настройку и поддержание серверов, команды могут сосредоточиться на том, что действительно важно — на разработке функций, которые приносят пользу конечным пользователям.
Основы AWS Lambda
AWS Lambda — это сервис бессерверных вычислений, который перевернул представление о том, как следует развертывать и масштабировать приложения в облаке. Чтобы в полной мере использовать потенциал этой технологии, необходимо понять, как она устроена и функционирует.
Принципы работы и архитектурные особенности
В основе AWS Lambda лежит концепция функций как основной единицы выполнения кода. Функция Lambda — это самодостаточный фрагмент кода, который выполняет конкретную задачу. Когда функция вызывается, AWS автоматически предоставляет для неё необходимые вычислительные ресурсы, запускает код и освобождает ресурсы после выполнения. Каждая Lambda-функция выполняется в изолированном контейнере с предустановленной средой выполнения. Для JavaScript разработчиков это обычно окружение Node.js, хотя AWS поддерживает и другие языки. Важно понимать, что контейнеры создаются и уничтожаются автоматически, это часть того, что делает Lambda по-настоящему «бессерверной».
Архитектурно функции Lambda следуют четкому жизненному циклу:
1. Инициализация — контейнер создаётся, загружается код функции, инициализируются внешние зависимости.
2. Вызов — функция обрабатывает входящее событие.
3. Завершение — результаты возвращаются, контейнер может быть уничтожен или сохранен для следующих вызовов.
Интересная особенность — контейнер функции может оставаться активным некоторое время после выполнения. Это позволяет обслуживать последующие запросы без повторной инициализации, что значительно снижает время отклика. Этот механизм называется «сохранением состояния» и является ключом к решению проблемы холодного старта, о которой мы поговорим позже.
Настройка среды разработки
Прежде чем погрузиться в разработку Lambda-функций, необходимо настроить рабочее окружение. Для JavaScript разработчика это означает установку Node.js, AWS CLI и, желательно, фреймворка для управления бессерверными приложениями.
Среди инструментов, которые значительно упрощают работу с Lambda, выделяются:
1. AWS SDK для JavaScript — официальный набор библиотек для взаимодействия с AWS сервисами из Node.js приложений.
2. Serverless Framework — один из самых популярных фреймворков, позволяющий декларативно описывать архитектуру приложения и автоматизировать процесс деплоя.
3. AWS SAM (Serverless Application Model) — открытый фреймворк от AWS для создания бессерверных приложений.
4. AWS CDK (Cloud Development Kit) — библиотека, позволяющая определять облачную инфраструктуру с помощью кода на TypeScript, JavaScript и других языках.
Выбор инструмента зависит от конкретного проекта и предпочтений команды. Например, если вы уже используете AWS CloudFormation, имеет смысл обратить внимание на AWS SAM, который построен поверх него. Если же вы хотите абстрагироваться от особенностей провайдера и иметь возможность в будущем мигрировать на другие платформы, Serverless Framework будет лучшим выбором. Базовая настройка среды разработки включает:
| Bash | 1
2
3
4
5
6
7
8
9
10
| # Установка Node.js и npm (если еще не установлены)
# Установка AWS CLI
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip && sudo ./aws/install
# Настройка учетных данных AWS
aws configure
# Установка Serverless Framework
npm install -g serverless |
|
После этого можно инициализировать новый проект:
| Bash | 1
2
| # Создание нового проекта с шаблоном Node.js
serverless create --template aws-nodejs --path my-service |
|
Стратегии деплоя Lambda-функций: CI/CD для бессерверных приложений
Непрерывная интеграция и доставка (CI/CD) критически важны для современных бессерверных приложений. Они позволяют автоматизировать тестирование и деплой, что особенно ценно в микросервисной архитектуре, где может быть множество отдельных функций.
Типичный CI/CD пайплайн для Lambda-функций включает:
1. Автоматизированное тестирование — модульные и интеграционные тесты для проверки работоспособности функций.
2. Пакетирование — упаковка кода и зависимостей в ZIP-архив или контейнер.
3. Деплой — развертывание функций в нужные среды (разработка, тестирование, продакшн).
4. Валидация — проверка работоспособности после деплоя.
AWS предоставляет несколько сервисов для построения таких пайплайнов: AWS CodePipeline, AWS CodeBuild и AWS CodeDeploy. Однако многие команды предпочитают использовать сторонние решения, такие как GitHub Actions, GitLab CI/CD или Jenkins. Пример простого CI/CD пайплайна с GitHub Actions для Lambda-функции на Node.js:
| YAML | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| name: Deploy Lambda
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
- run: npm ci
- run: npm test
- name: Deploy
uses: serverless/github-action@v3
with:
args: deploy --stage prod
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} |
|
Важно помнить о стратегии деплоя — как проводить обновления без простоев и с минимальным риском. AWS Lambda поддерживает несколько моделей деплоя, включая постепенное развертывание с возможностью отката, что значительно снижает риски при обновлении функций.
Модель выполнения JavaScript в Lambda
JavaScript в AWS Lambda выполняется внутри контейнеров с использованием Node.js. Когда мы говорим о Node.js в Lambda, важно понимать некоторые особенности его работы в этой среде.
Во-первых, функция обработчика в Lambda должна следовать определенной сигнатуре:
| JavaScript | 1
2
3
4
5
6
7
| exports.handler = async (event, context) => {
// Логика функции
return {
statusCode: 200,
body: JSON.stringify({ message: "Hello, World!" }),
};
}; |
|
Параметр event содержит данные о событии, которое вызвало функцию — это может быть HTTP-запрос, сообщение из очереди или изменение в базе данных. Параметр context предоставляет информацию о среде выполнения, включая оставшееся время до таймаута и идентификатор запроса. Важной особенностью выполнения JavaScript в Lambda является механизм обработки асинхронных операций. AWS Lambda ждет завершения обработчика перед возвратом результата, поэтому нужно правильно использовать асинхронные паттерны. В современном JavaScript для этого используются Promise и async/await. Другой важный аспект — управление памятью. JavaScript — язык с автоматическим управлением памятью через сборщик мусора, но в контексте Lambda это может создавать определенные проблемы. Глобальные переменные, объявленные вне функции обработчика, сохраняются между вызовами в рамках одного контейнера, что можно использовать для кэширования, но также это может привести к утечкам памяти, если не управлять ими правильно.
Модель безопасности в AWS Lambda и управление правами доступа
Когда речь заходит о безопасности в бессерверных приложениях, AWS Lambda предлагает многоуровневую модель защиты. В её основе лежит принцип минимальных привилегий — предоставление функции только тех прав, которые ей действительно необходимы для работы. Ключевым компонентом модели безопасности Lambda является роль выполнения (execution role). Это роль IAM (Identity and Access Management), которая определяет, какие действия может совершать ваша функция по отношению к другим сервисам AWS. Например, если ваша функция должна читать данные из DynamoDB и отправлять сообщения в SQS очередь, роль должна включать соответствующие разрешения, но не более того.
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| // Пример политики IAM для Lambda функции в формате JSON
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:GetItem",
"dynamodb:Query"
],
"Resource": "arn:aws:dynamodb:region:account-id:table/my-table"
},
{
"Effect": "Allow",
"Action": [
"sqs:SendMessage"
],
"Resource": "arn:aws:sqs:region:account-id:my-queue"
}
]
} |
|
Помимо ролей IAM, важную роль в обеспечении безопасности играют переменные окружения. Они позволяют хранить конфигурационные параметры и чувствительные данные вне кода функции. AWS Lambda поддерживает шифрование переменных окружения для защиты секретной информации, такой как API ключи или пароли к базам данных.
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| // Использование переменных окружения в коде функции
exports.handler = async (event) => {
// API_KEY хранится в зашифрованных переменных окружения
const apiKey = process.env.API_KEY;
// Использование API ключа для внешнего запроса
const response = await fetch('https://api.example.com/data', {
headers: {
'Authorization': [INLINE]Bearer ${apiKey}[/INLINE]
}
});
// Обработка ответа
const data = await response.json();
return data;
}; |
|
Безопасность сетевого уровня также критически важна. По умолчанию Lambda функции выполняются в управляемой AWS VPC (Virtual Private Cloud) с доступом в интернет. Однако функции можно настроить на работу в вашей собственной VPC, что позволяет контролировать сетевой трафик и обеспечивать доступ к ресурсам, недоступным из публичного интернета, например, к базам данных в приватных подсетях. Стоит отметить особенности работы функций в VPC. При такой конфигурации Lambda создаёт эластичные сетевые интерфейсы в выбранных подсетях, что может увеличить время холодного старта. Для оптимизации производительности рекомендуется настраивать совместное использование ENI (Elastic Network Interface) между функциями и размещать их в подсетях с достаточным количеством доступных IP-адресов.
Мониторинг безопасности — ещё один важный аспект. AWS CloudTrail автоматически регистрирует все API вызовы к Lambda, что позволяет отслеживать, кто и когда вносил изменения в функции. Для обнаружения потенциальных угроз и аномалий можно настроить Amazon GuardDuty, который анализирует логи CloudTrail, VPC Flow Logs и DNS логи.
При создании многоуровневых приложений часто используется паттерн "функция к функции", когда одна Lambda вызывает другую. В таких сценариях особенно важно чётко определять границы доверия и использовать механизмы авторизации между функциями.
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // Вызов другой Lambda функции из JavaScript
const AWS = require('aws-sdk');
const lambda = new AWS.Lambda();
exports.handler = async (event) => {
const params = {
FunctionName: 'my-other-function',
InvocationType: 'RequestResponse',
Payload: JSON.stringify({ data: 'some data' })
};
const result = await lambda.invoke(params).promise();
return JSON.parse(result.Payload);
}; |
|
JavaScript разработчики должны особое внимание уделять обработке ошибок и валидации входных данных. Node.js приложения уязвимы к атакам типа NoSQL-инъекций и прототипного загрязнения (prototype pollution), поэтому крайне важно проверять и санитизировать все входные данные перед их использованием. В контексте API, построенных на AWS Lambda, также необходимо настраивать прокси-уровень (обычно API Gateway) с механизмами аутентификации и авторизации. API Gateway поддерживает несколько методов авторизации: API ключи, IAM роли, Lambda авторизаторы и интеграцию с Amazon Cognito. Lambda авторизаторы особенно гибки, поскольку позволяют реализовать практически любую логику проверки доступа:
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
| // Пример Lambda авторизатора для API Gateway
exports.handler = async (event) => {
const token = event.authorizationToken;
try {
// Проверка токена (может включать вызов внешних сервисов)
const decodedToken = verifyToken(token);
// Формирование политики доступа
const policyDocument = {
Version: '2012-10-17',
Statement: [
{
Action: 'execute-api:Invoke',
Effect: decodedToken ? 'Allow' : 'Deny',
Resource: event.methodArn
}
]
};
return {
principalId: decodedToken?.sub || 'anonymous',
policyDocument,
context: {
// Дополнительные данные, которые будут переданы целевой Lambda
userId: decodedToken?.sub
}
};
} catch (err) {
// В случае ошибки при проверке токена запрещаем доступ
return {
principalId: 'anonymous',
policyDocument: {
Version: '2012-10-17',
Statement: [
{
Action: 'execute-api:Invoke',
Effect: 'Deny',
Resource: event.methodArn
}
]
}
};
}
}; |
|
Наконец, стоит упомянуть о регулярном аудите безопасности и сканировании зависимостей. В экосистеме Node.js существуют инструменты, такие как npm audit, которые помогают выявлять уязвимости в используемых пакетах. Включение таких проверок в CI/CD пайплайн позволяет обнаруживать проблемы до того, как код попадет в продакшн.
Как написать пайтон код в github workflow для пуша его в AWS Lambda есть Экшен в воркфлоу, настроенный и подключённый. Есть у меня такая вот часть скрипта
- name: AWS Lambda Deploy
uses:... Как верно использовать JavaScript native Fetch API вместо jQuery для опроса данных из API Я разбираюсь в js, и на данный момент понял что метод из js Fetch может сам отправлять запросы на сервер и динамически подгружать данные по мере... JavaScript and API Добрый день. Столкнулся с проблемой, которая никак не поддается моему решению.
Во время написания достаточно легкой страницы я запихнул в класс... Разработка Web API Требуется разработать web-api с настройкой серверной части
Прикладываю ТЗ с примером api
Задание:
1. Разработка api методов
2. Настройка...
Немного практики
Создание работающего API с использованием бессерверной архитектуры — процесс, который включает несколько ключевых этапов, начиная от написания самой функции и заканчивая интеграцией с другими сервисами AWS.
Создание первой Lambda-функции на JavaScript
Создать Lambda-функцию можно как через консоль AWS, так и с помощью инструментов командной строки или фреймворков типа Serverless. Начнем с простого примера — API для приветствия пользователя. Вот базовый код функции:
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
| exports.handler = async (event) => {
const name = event.queryStringParameters?.name || "гость";
return {
statusCode: 200,
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
message: `Привет, ${name}!`,
timestamp: new Date().toISOString()
})
};
}; |
|
Эта функция извлекает параметр name из строки запроса и возвращает JSON-ответ. Если параметр отсутствует, используется значение по умолчанию — "гость".
Для создания функции через консоль AWS:
1. Откройте консоль AWS и перейдите в сервис Lambda.
2. Нажмите "Создать функцию".
3. Выберите "Автор с нуля".
4. Укажите имя функции, например, hello-world-api.
5. Выберите среду выполнения Node.js (последняя стабильная версия).
6. Создайте роль с базовыми разрешениями Lambda.
7. Нажмите "Создать функцию".
8. В редакторе кода вставьте приведенный выше пример.
9. Сохраните функцию.
Для тестирования функции можно создать тестовое событие, имитирующее HTTP-запрос:
| JSON | 1
2
3
4
5
| {
"queryStringParameters": {
"name": "Анна"
}
} |
|
После запуска теста вы должны получить ответ с приветствием "Привет, Анна!".
Для более сложных сценариев лучше использовать локальную разработку с последующим деплоем. Это позволяет работать в привычном IDE, использовать системы контроля версий и автоматизировать тестирование.
Управление зависимостями и пакетами в бессерверной среде
Почти любое современное JavaScript-приложение использует внешние библиотеки. В контексте AWS Lambda это создает определенные трудности, поскольку все зависимости должны быть упакованы вместе с кодом функции. Основной инструмент для управления зависимостями в Node.js — npm или yarn. При создании Lambda-функции необходимо включить все зависимости в пакет деплоя.
Пример структуры проекта:
| Code | 1
2
3
4
| my-lambda/
├── node_modules/
├── index.js
└── package.json |
|
Файл package.json содержит информацию о проекте и его зависимостях:
| JSON | 1
2
3
4
5
6
7
8
9
10
| {
"name": "my-lambda-function",
"version": "1.0.0",
"description": "Пример Lambda функции",
"main": "index.js",
"dependencies": {
"axios": "^0.24.0",
"uuid": "^8.3.2"
}
} |
|
При подготовке функции к деплою необходимо установить зависимости в режиме production:
| Bash | 1
| npm install --production |
|
Затем весь каталог нужно заархивировать и загрузить в Lambda. Однако для больших проектов размер пакета может превысить ограничение Lambda в 50 МБ (для несжатого кода). В таких случаях можно использовать:
1. Слои Lambda (Lambda Layers) — специальные пакеты с библиотеками, которые можно подключать к функциям.
2. Контейнеры Docker — Lambda поддерживает запуск функций в контейнерах.
3. Инструменты минимизации — например, webpack для уменьшения размера пакета.
Пример использования webpack для оптимизации размера пакета:
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| // webpack.config.js
const path = require('path');
module.exports = {
target: 'node',
mode: 'production',
entry: './index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js',
libraryTarget: 'commonjs2'
},
// Исключаем AWS SDK из бандла, так как он доступен в среде Lambda
externals: ['aws-sdk']
}; |
|
После настройки webpack можно собрать оптимизированную версию функции:
В результате получится минифицированный файл, содержащий ваш код и только действительно используемые части зависимостей.
Интеграция с API Gateway
API Gateway — это сервис AWS, который позволяет создавать, публиковать и управлять RESTful API. Он выступает в роли "фронт-двери" для ваших Lambda-функций, маршрутизируя HTTP-запросы к соответствующим обработчикам.
Для создания полноценного API необходимо:
1. Создать API в API Gateway.
2. Определить ресурсы и методы (например, GET /users, POST /users).
3. Настроить интеграцию методов с Lambda-функциями.
4. Настроить маппинги запросов и ответов.
5. Развернуть API на выбранной стадии (dev, prod и т.д.).
API Gateway преобразует HTTP-запросы в события, которые передаются в Lambda-функцию. Вот пример такого события:
| JSON | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| {
"resource": "/users",
"path": "/users",
"httpMethod": "GET",
"headers": {
"Accept": "*/*",
"Authorization": "Bearer eyJ..."
},
"queryStringParameters": {
"page": "1",
"limit": "10"
},
"pathParameters": null,
"body": null,
"isBase64Encoded": false
} |
|
Для обработки таких событий Lambda-функция должна извлекать нужную информацию и формировать правильный ответ:
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| exports.handler = async (event) => {
// Получаем параметры запроса
const page = parseInt(event.queryStringParameters?.page || '1');
const limit = parseInt(event.queryStringParameters?.limit || '10');
try {
// Здесь логика получения данных (например, из DynamoDB)
const users = await fetchUsers(page, limit);
return {
statusCode: 200,
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
users,
pagination: {
page,
limit,
totalPages: Math.ceil(users.total / limit)
}
})
};
} catch (error) {
console.error('Error fetching users:', error);
return {
statusCode: 500,
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
error: "Произошла ошибка при получении данных"
})
};
}
}; |
|
С помощью Serverless Framework можно декларативно описать как Lambda-функции, так и настройки API Gateway в одном файле:
| YAML | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| # serverless.yml
service: user-api
provider:
name: aws
runtime: nodejs14.x
region: eu-central-1
functions:
getUsers:
handler: handlers/users.getUsers
events:
- http:
path: users
method: get
cors: true
getUserById:
handler: handlers/users.getUserById
events:
- http:
path: users/{id}
method: get
cors: true
request:
parameters:
paths:
id: true |
|
Такой подход значительно упрощает создание и управление API, особенно в проектах с множеством эндпоинтов.
Работа с базами данных и хранилищами
В бессерверной архитектуре традиционные реляционные базы данных часто заменяются управляемыми сервисами, такими как Amazon DynamoDB — NoSQL база данных с высокой производительностью и масштабируемостью. Для работы с DynamoDB из Node.js используется AWS SDK:
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
| const AWS = require('aws-sdk');
const dynamoDB = new AWS.DynamoDB.DocumentClient();
// Функция для получения пользователя по ID
async function getUser(userId) {
const params = {
TableName: 'Users',
Key: {
id: userId
}
};
try {
const result = await dynamoDB.get(params).promise();
return result.Item;
} catch (error) {
console.error('Error getting user:', error);
throw error;
}
}
// Lambda-функция
exports.handler = async (event) => {
const userId = event.pathParameters?.id;
if (!userId) {
return {
statusCode: 400,
body: JSON.stringify({ error: 'ID пользователя не указан' })
};
}
try {
const user = await getUser(userId);
if (!user) {
return {
statusCode: 404,
body: JSON.stringify({ error: 'Пользователь не найден' })
};
}
return {
statusCode: 200,
body: JSON.stringify(user)
};
} catch (error) {
return {
statusCode: 500,
body: JSON.stringify({ error: 'Внутренняя ошибка сервера' })
};
}
}; |
|
Помимо DynamoDB, часто используются:
1. Amazon S3 для хранения файлов и статического контента.
2. Amazon Aurora Serverless для реляционных баз данных.
3. Amazon ElastiCache для кэширования.
Важный аспект при работе с базами данных в Lambda — управление соединениями. Поскольку функция может быть запущена в нескольких контейнерах параллельно, а контейнеры могут быть повторно использованы, эффективное управление пулом соединений становится критически важным.
При работе с реляционными базами данных, такими как PostgreSQL или MySQL, через AWS RDS, важно правильно управлять соединениями. Рекомендуемая стратегия — инициализировать соединение вне обработчика функции и повторно использовать его между вызовами:
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| const { Pool } = require('pg');
// Соединение инициализируется один раз, при загрузке модуля
const pool = new Pool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
});
// Проверяем соединение при инициализации
let isConnected = false;
pool.connect()
.then(() => {
isConnected = true;
console.log('Соединение с базой данных установлено');
})
.catch(err => {
console.error('Ошибка подключения к базе данных:', err);
});
exports.handler = async (event) => {
// Используем существующее соединение
if (!isConnected) {
await pool.connect();
isConnected = true;
}
// Код для работы с базой данных
const result = await pool.query('SELECT * FROM users LIMIT 10');
return {
statusCode: 200,
body: JSON.stringify(result.rows)
};
}; |
|
Обработка асинхронных событий и очередей сообщений в JavaScript Lambda
Помимо обработки HTTP-запросов, Lambda-функции могут реагировать на множество других типов событий, включая:
1. Изменения в хранилище S3 (загрузка или удаление файлов).
2. Сообщения в очередях SQS или потоках Kinesis.
3. События из других сервисов AWS через EventBridge.
Рассмотрим пример обработки сообщений из очереди Amazon SQS. Это распространённый сценарий для асинхронных задач, таких как обработка фоновых заданий или интеграция между системами.
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
| exports.handler = async (event) => {
// event.Records содержит массив сообщений из SQS
const records = event.Records;
for (const record of records) {
try {
// Получаем тело сообщения
const messageBody = JSON.parse(record.body);
// Обрабатываем сообщение в зависимости от его типа
switch (messageBody.type) {
case 'USER_REGISTERED':
await sendWelcomeEmail(messageBody.user);
break;
case 'ORDER_PLACED':
await processOrder(messageBody.order);
break;
default:
console.warn(`Неизвестный тип сообщения: ${messageBody.type}`);
}
} catch (error) {
// Логируем ошибку, но продолжаем обработку других сообщений
console.error('Ошибка обработки сообщения:', error);
console.error('Проблемное сообщение:', record.body);
}
}
// Возвращаем успешный результат
return {
batchItemFailures: [] // Если нужно отметить конкретные сообщения как необработанные
};
};
async function sendWelcomeEmail(user) {
// Логика отправки письма
console.log(`Отправка приветственного письма для ${user.email}`);
}
async function processOrder(order) {
// Логика обработки заказа
console.log(`Обработка заказа №${order.id} на сумму ${order.total}`);
} |
|
В этом примере Lambda-функция обрабатывает сообщения из очереди SQS, анализирует их содержимое и выполняет соответствующие действия. Важно обрабатывать каждое сообщение в блоке try-catch, чтобы ошибка в одном сообщении не прерывала обработку всей пачки.
Для сложных сценариев обработки событий можно использовать AWS Step Functions — сервис, который позволяет создавать потоки выполнения с последовательными, параллельными шагами и условной логикой. Пример определения простого рабочего потока с помощью Step Functions:
| JSON | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
| {
"Comment": "Процесс обработки заказа",
"StartAt": "ProcessPayment",
"States": {
"ProcessPayment": {
"Type": "Task",
"Resource": "arn:aws:lambda:region:account:function:process-payment",
"Next": "PaymentSuccessful",
"Catch": [
{
"ErrorEquals": ["PaymentError"],
"Next": "NotifyPaymentFailure"
}
]
},
"PaymentSuccessful": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.paymentSuccessful",
"BooleanEquals": true,
"Next": "FulfillOrder"
}
],
"Default": "NotifyPaymentFailure"
},
"FulfillOrder": {
"Type": "Task",
"Resource": "arn:aws:lambda:region:account:function:fulfill-order",
"Next": "NotifyCustomer"
},
"NotifyPaymentFailure": {
"Type": "Task",
"Resource": "arn:aws:lambda:region:account:function:notify-payment-failure",
"End": true
},
"NotifyCustomer": {
"Type": "Task",
"Resource": "arn:aws:lambda:region:account:function:notify-customer",
"End": true
}
}
} |
|
Такой подход позволяет разделить сложную бизнес-логику на отдельные функции Lambda, каждая из которых имеет четкую ответственность. Это упрощает тестирование, отладку и поддержку приложения.
Для обработки более интенсивных потоков данных можно использовать Amazon Kinesis совместно с Lambda. Это позволяет обрабатывать потоковые данные в режиме реального времени, например, логи приложений, телеметрию или данные с сенсоров.
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| exports.handler = async (event) => {
// event.Records содержит записи из Kinesis
for (const record of event.Records) {
// Данные закодированы в Base64
const payload = Buffer.from(record.kinesis.data, 'base64').toString('utf-8');
try {
const data = JSON.parse(payload);
// Обработка данных
await processMetric(data);
} catch (error) {
console.error('Ошибка обработки записи Kinesis:', error);
}
}
};
async function processMetric(data) {
// Логика обработки метрики
console.log(`Обработка метрики: ${data.metricName}, значение: ${data.value}`);
// Например, сохранение в базу данных или агрегация
} |
|
Важным аспектом при работе с асинхронными событиями является обработка ошибок и повторных попыток. В зависимости от источника события, AWS предлагает разные модели:
1. Для SQS — встроенная очередь "неудачных" сообщений (Dead Letter Queue).
2. Для Kinesis — возможность начать обработку с определенной позиции.
3. Для Lambda, вызываемой асинхронно — политики повторных попыток и DLQ.
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| // Для асинхронного вызова Lambda можно настроить обработку ошибок
const AWS = require('aws-sdk');
const lambda = new AWS.Lambda();
exports.handler = async (event) => {
const params = {
FunctionName: 'target-function',
InvocationType: 'Event', // Асинхронный вызов
Payload: JSON.stringify({
data: 'some-data'
})
};
try {
await lambda.invoke(params).promise();
return { success: true };
} catch (error) {
console.error('Ошибка вызова Lambda:', error);
return { success: false, error: error.message };
}
}; |
|
При проектировании архитектуры, основанной на событиях, важно учитывать возможность повторной доставки. Некоторые источники событий, такие как SQS, не гарантируют ровно однократную доставку (exactly-once delivery) и могут доставить одно сообщение несколько раз. Поэтому операции должны быть идемпотентными — повторное выполнение не должно приводить к нежелательным эффектам.
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| // Пример идемпотентной обработки платежа
exports.handler = async (event) => {
const { paymentId, amount } = JSON.parse(event.Records[0].body);
// Проверяем, не был ли платеж уже обработан
const existingPayment = await checkPaymentExists(paymentId);
if (existingPayment) {
console.log(`Платеж ${paymentId} уже был обработан ранее, пропускаем`);
return { success: true };
}
// Обрабатываем платеж
await processPayment(paymentId, amount);
// Сохраняем запись о успешной обработке
await markPaymentAsProcessed(paymentId);
return { success: true };
}; |
|
Оптимизация и масштабирование
Бессерверные приложения на AWS Lambda обладают невероятным потенциалом масштабирования, но для получения максимальной производительности необходимо глубокое понимание механизмов их работы и правильная оптимизация. Существует несколько ключевых областей, требующих особого внимания.
Управление холодным стартом
Холодный старт — пожалуй, самая распространённая проблема в работе с Lambda-функциями. Она возникает, когда функция запускается впервые или после периода бездействия, и AWS нужно создать новый контейнер для её выполнения. Это может занимать от нескольких сотен миллисекунд до нескольких секунд. Для JavaScript разработчиков проблема усугубляется тем что Node.js не самый быстрый при инициализации среди поддерживаемых Lambda рантаймов. К счастью, существует ряд техник для минимизации этого эффекта:
1. Выбор оптимального объема памяти. Lambda выделяет вычислительные ресурсы пропорционально заданному объему памяти. Чем больше памяти, тем больше CPU выделяется функции, что может значительно ускорить холодный запуск:
| JavaScript | 1
2
3
4
| // Пример конфигурации в serverless.yml
provider:
name: aws
memorySize: 1024 // Выделение 1 ГБ памяти также дает больше CPU |
|
2. Использование Provisioned Concurrency. Эта функциональность, представленная AWS в 2019 году, позволяет заранее инициализировать заданное количество экземпляров функции:
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
| // Настройка в CloudFormation:
Resources:
MyLambdaFunction:
Type: AWS::Lambda::Function
Properties:
...
MyProvisionedConcurrency:
Type: AWS::Lambda::ProvisionedConcurrencyConfig
Properties:
FunctionName: !Ref MyLambdaFunction
ProvisionedConcurrentExecutions: 5
Qualifier: LIVE |
|
3. Оптимизация кода функции. Сделайте код максимально эффективным, особенно части, выполняемые при инициализации:
- Импортируйте только необходимые модули.
- Используйте деструктуризацию для импорта конкретных методов.
- Минимизируйте глобальные переменные и тяжелые вычисления вне обработчика.
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // Вместо этого
const AWS = require('aws-sdk');
const s3 = new AWS.S3();
const dynamodb = new AWS.DynamoDB.DocumentClient();
// Используйте это
const { S3, DynamoDB } = require('aws-sdk');
const s3 = new S3();
const dynamodb = new DynamoDB.DocumentClient();
// Или еще лучше, импортируйте только нужные сервисы
// Например, если вам нужен только S3:
const { S3 } = require('aws-sdk');
const s3 = new S3(); |
|
4. Разделение кода на слои: используйте Lambda Layers для общих зависимостей, которые используются несколькими функциями. Это не только сокращает размер пакета деплоя, но и может улучшить время инициализации, так как слои часто кэшируются между функциями.
5. Упаковка только необходимого кода. Используйте webpack, esbuild или подобные инструменты для удаления неиспользуемого кода и уменьшения размера пакета:
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
| // Использование esbuild для оптимизации пакета Lambda
const esbuild = require('esbuild');
esbuild.build({
entryPoints: ['index.js'],
bundle: true,
minify: true,
sourcemap: false,
platform: 'node',
target: ['node14'],
outfile: 'dist/index.js',
external: ['aws-sdk'], // AWS SDK доступен в среде Lambda
}).catch(() => process.exit(1)); |
|
Техники оптимизации производительности
Помимо борьбы с холодным стартом, существует много других способов повысить производительность бессерверных API:
1. Асинхронные операции и параллельное выполнение. Node.js отлично справляется с асинхронностью, используйте это преимущество:
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| // Вместо последовательного выполнения
async function getDataSequential(userId) {
const userData = await getUserData(userId);
const userOrders = await getUserOrders(userId);
const userPreferences = await getUserPreferences(userId);
return { userData, userOrders, userPreferences };
}
// Используйте параллельное выполнение
async function getDataParallel(userId) {
const [userData, userOrders, userPreferences] = await Promise.all([
getUserData(userId),
getUserOrders(userId),
getUserPreferences(userId)
]);
return { userData, userOrders, userPreferences };
} |
|
2. Оптимизация взаимодействия с внешними сервисами:
- Используйте пакетную обработку (например, BatchGetItem в DynamoDB вместо множества отдельных GetItem).
- Избегайте блокирующих I/O операций.
- Минимизируйте количество сетевых запросов.
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| // Не эффективно: множественные запросы к DynamoDB
async function getMultipleItemsInefficient(ids) {
const results = [];
for (const id of ids) {
const params = {
TableName: 'MyTable',
Key: { id }
};
const result = await dynamodb.get(params).promise();
if (result.Item) results.push(result.Item);
}
return results;
}
// Оптимизировано: один запрос BatchGetItem
async function getMultipleItemsOptimized(ids) {
const params = {
RequestItems: {
'MyTable': {
Keys: ids.map(id => ({ id }))
}
}
};
const result = await dynamodb.batchGet(params).promise();
return result.Responses.MyTable || [];
} |
|
3. Использование бинарных форматов. Для обмена данными между сервисами JSON не всегда оптимален. Форматы вроде Protocol Buffers или MessagePack могут значительно уменьшить размер данных и время сериализации:
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| // Пример с использованием MessagePack
const msgpack = require('msgpack');
exports.handler = async (event) => {
// Получаем данные
const data = { /* большой объект данных */ };
// Сериализуем в компактный бинарный формат
const binaryData = msgpack.pack(data);
// Кодируем в Base64 для передачи по HTTP
const base64Data = Buffer.from(binaryData).toString('base64');
return {
statusCode: 200,
headers: {
'Content-Type': 'application/octet-stream'
},
body: base64Data,
isBase64Encoded: true
};
}; |
|
Стратегии кэширования для повышения производительности Lambda-функций
Кэширование — один из самых эффективных способов оптимизации производительности бессерверных приложений. В контексте AWS Lambda существует несколько уровней кэширования:
1. Внутрифункциональное кэширование. Используйте область вне обработчика для сохранения данных между вызовами функции внутри одного контейнера:
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| // Простой кэш с временем жизни (TTL)
const cache = {};
function getCachedData(key, ttlMs, fetchFunction) {
const now = Date.now();
if (cache[key] && cache[key].expiry > now) {
console.log(`Используем кэшированные данные для ключа ${key}`);
return Promise.resolve(cache[key].data);
}
console.log(`Получаем свежие данные для ключа ${key}`);
return fetchFunction().then(data => {
cache[key] = {
data,
expiry: now + ttlMs
};
return data;
});
}
exports.handler = async (event) => {
const userId = event.pathParameters?.userId;
const userData = await getCachedData(
[INLINE]user_${userId}[/INLINE],
60000, // TTL: 1 минута
() => fetchUserFromDatabase(userId)
);
return {
statusCode: 200,
body: JSON.stringify(userData)
};
}; |
|
2. ElastiCache и DAX. Для более серьезных сценариев используйте выделенные сервисы кэширования:
- Amazon ElastiCache (Redis/Memcached) для общего кэширования.
- DynamoDB Accelerator (DAX) для кэширования чтения из DynamoDB.
3. API Gateway Cache. API Gateway может кэшировать ответы Lambda-функций на уровне API:
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| # Пример настройки кэширования в Terraform
resource "aws_api_gateway_stage" "example" {
rest_api_id = aws_api_gateway_rest_api.example.id
stage_name = "prod"
cache_cluster_enabled = true
cache_cluster_size = "0.5" # Размер кэша в ГБ (возможные значения: 0.5, 1.6, 6.1, 13.5, 28.4, 58.2, 118, 237)
}
# Настройка кэширования для конкретного метода
resource "aws_api_gateway_method_settings" "example" {
rest_api_id = aws_api_gateway_rest_api.example.id
stage_name = aws_api_gateway_stage.example.stage_name
method_path = "*/*" # Можно указать конкретный путь, например "resource/GET"
settings {
cache_data_encrypted = true
cache_ttl_in_seconds = 300 # Время жизни кэша (5 минут)
caching_enabled = true
}
} |
|
Мониторинг и отладка
Эффективная оптимизация невозможна без понимания того, как работает ваше приложение. AWS предоставляет ряд инструментов для мониторинга и отладки Lambda-функций:
1. AWS CloudWatch. Основной инструмент для мониторинга Lambda. Автоматически собирает метрики производительности:
- Количество вызовов.
- Продолжительность выполнения.
- Количество ошибок.
- Использование памяти.
CloudWatch Logs также сохраняет вывод функций, включая console.log и ошибки.
2. AWS X-Ray. Позволяет визуализировать потоки запросов через различные сервисы AWS, включая Lambda:
| JavaScript | 1
2
3
4
5
6
| // Включение X-Ray трассировки в Lambda
const AWSXRay = require('aws-xray-sdk-core');
const AWS = AWSXRay.captureAWS(require('aws-sdk'));
// Теперь все вызовы AWS сервисов будут трассироваться
const dynamoDB = new AWS.DynamoDB.DocumentClient(); |
|
3. Создание собственных метрик. Вы можете отправлять пользовательские метрики в CloudWatch:
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
| const AWS = require('aws-sdk');
const cloudwatch = new AWS.CloudWatch();
async function sendMetric(name, value, unit = 'Count') {
const params = {
MetricData: [
{
MetricName: name,
Value: value,
Unit: unit,
Dimensions: [
{
Name: 'FunctionName',
Value: process.env.AWS_LAMBDA_FUNCTION_NAME
}
],
Timestamp: new Date()
}
],
Namespace: 'MyApplicationMetrics'
};
return cloudwatch.putMetricData(params).promise();
}
exports.handler = async (event) => {
const startTime = process.hrtime();
try {
// Бизнес-логика
const result = await processData(event);
// Отправляем метрику успешного выполнения
await sendMetric('SuccessfulExecution', 1);
// Измеряем время выполнения
const [seconds, nanoseconds] = process.hrtime(startTime);
const duration = seconds * 1000 + nanoseconds / 1000000;
await sendMetric('ProcessingTime', duration, 'Milliseconds');
return result;
} catch (error) {
// Отправляем метрику ошибки
await sendMetric('FailedExecution', 1);
throw error;
}
}; |
|
Примеры из практики
Теория и рекомендации хороши, но ничто не заменит реальных примеров. Рассмотрим несколько практических сценариев использования бессерверных API на AWS Lambda и JavaScript, а также способы решения типичных задач и проблем.
Кейсы применения
Обработка изображений в реальном времени
Одно из самых распространённых применений Lambda — автоматическая обработка изображений. Представьте сервис, который позволяет пользователям загружать фотографии, а затем автоматически создаёт их миниатюры и версии с водяными знаками.
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
| const AWS = require('aws-sdk');
const sharp = require('sharp');
const s3 = new AWS.S3();
exports.handler = async (event) => {
// Получаем информацию о загруженном файле
const bucket = event.Records[0].s3.bucket.name;
const key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' '));
// Игнорируем уже обработанные файлы
if (key.includes('thumbnail_') || key.includes('watermark_')) {
console.log('Файл уже обработан, пропускаем');
return;
}
try {
// Получаем изображение из S3
const imageData = await s3.getObject({ Bucket: bucket, Key: key }).promise();
// Создаём миниатюру
const thumbnail = await sharp(imageData.Body)
.resize(200, 200, { fit: 'inside' })
.toBuffer();
// Создаём версию с водяным знаком
const watermark = await sharp(imageData.Body)
.composite([{
input: Buffer.from('<svg>...</svg>'), // Водяной знак в формате SVG
gravity: 'southeast'
}])
.toBuffer();
// Загружаем обработанные изображения обратно в S3
await Promise.all([
s3.putObject({
Bucket: bucket,
Key: [INLINE]thumbnail_${key}[/INLINE],
Body: thumbnail,
ContentType: imageData.ContentType
}).promise(),
s3.putObject({
Bucket: bucket,
Key: [INLINE]watermark_${key}[/INLINE],
Body: watermark,
ContentType: imageData.ContentType
}).promise()
]);
console.log(`Успешно обработано изображение: ${key}`);
} catch (error) {
console.error(`Ошибка обработки ${key}: ${error.message}`);
throw error;
}
}; |
|
Этот подход очень эффективен — вы платите только за фактическое время обработки, система автоматически масштабируется при пиковых нагрузках (например, когда множество пользователей одновременно загружают фотографии), а обработка происходит параллельно.
Платёжный API с высокой надёжностью
Другой показательный пример — построение платёжного API, где критически важны безопасность, надёжность и масштабируемость.
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
| const AWS = require('aws-sdk');
const { v4: uuidv4 } = require('uuid');
const dynamoDB = new AWS.DynamoDB.DocumentClient();
const sns = new AWS.SNS();
exports.handler = async (event) => {
// Парсим тело запроса
const body = JSON.parse(event.body);
const { amount, cardToken, customerId } = body;
// Идентификатор транзакции для идемпотентности
const transactionId = body.transactionId || uuidv4();
try {
// Проверяем, не обработана ли уже транзакция
const existingTx = await dynamoDB.get({
TableName: 'Transactions',
Key: { id: transactionId }
}).promise();
if (existingTx.Item) {
return {
statusCode: 200,
body: JSON.stringify({
success: true,
transactionId,
message: 'Транзакция уже была обработана',
status: existingTx.Item.status
})
};
}
// Обрабатываем платеж через платежный шлюз
// В реальности здесь был бы код интеграции с Stripe, PayPal и т.д.
const paymentResult = await processPayment(amount, cardToken);
// Сохраняем результат в базу данных
await dynamoDB.put({
TableName: 'Transactions',
Item: {
id: transactionId,
customerId,
amount,
status: paymentResult.status,
processorId: paymentResult.id,
createdAt: new Date().toISOString()
}
}).promise();
// Отправляем уведомление о платеже
await sns.publish({
TopicArn: process.env.PAYMENT_NOTIFICATION_TOPIC,
Message: JSON.stringify({
transactionId,
customerId,
amount,
status: paymentResult.status
})
}).promise();
return {
statusCode: 200,
body: JSON.stringify({
success: true,
transactionId,
status: paymentResult.status
})
};
} catch (error) {
console.error(`Ошибка при обработке платежа: ${error.message}`);
// В случае ошибки сохраняем информацию о неудачной попытке
await dynamoDB.put({
TableName: 'Transactions',
Item: {
id: transactionId,
customerId,
amount,
status: 'ERROR',
error: error.message,
createdAt: new Date().toISOString()
}
}).promise();
return {
statusCode: 500,
body: JSON.stringify({
success: false,
message: 'Не удалось обработать платеж',
transactionId
})
};
}
}; |
|
В этом примере мы используем идемпотентность для предотвращения дублирования платежей и храним всю информацию о транзакциях в DynamoDB. SNS используется для асинхронного уведомления других систем о результате платежа.
Типичные проблемы и их решения
Проблема: Долгое время выполнения и таймауты
Lambda имеет ограничение на максимальное время выполнения (15 минут). Для операций, которые могут занимать больше времени, необходимо разбивать их на этапы.
Решение: Проектирование с использованием Step Functions
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
| // Функция запуска процесса
exports.startProcessing = async (event) => {
const { dataId } = JSON.parse(event.body);
// Запускаем Step Functions
const stepFunctions = new AWS.StepFunctions();
const result = await stepFunctions.startExecution({
stateMachineArn: process.env.STATE_MACHINE_ARN,
input: JSON.stringify({ dataId })
}).promise();
return {
statusCode: 202, // Accepted
body: JSON.stringify({
message: 'Процесс начат',
executionArn: result.executionArn
})
};
};
// Одна из функций в процессе
exports.processChunk = async (event) => {
const { dataId, chunkNumber, totalChunks } = event;
// Обработка одной части данных
await processDataChunk(dataId, chunkNumber);
if (chunkNumber < totalChunks) {
// Возвращаем результат для следующей итерации
return {
dataId,
chunkNumber: chunkNumber + 1,
totalChunks
};
} else {
// Обработка завершена
return {
dataId,
status: 'completed'
};
}
}; |
|
Проблема: Ограничения размера пакета деплоя
Lambda имеет лимит на размер пакета деплоя (50 МБ для .zip, 10 ГБ для контейнера).
Решение: Lambda Layers и оптимизация размера пакета
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| // serverless.yml
provider:
name: aws
runtime: nodejs14.x
layers:
commonLibs:
path: layers/common
description: Общие библиотеки для всех функций
functions:
api:
handler: src/api.handler
layers:
- {Ref: CommonLibsLambdaLayer}
package:
individually: true
exclude:
- node_modules/[B]
- layers/[/B]
- package.json
- package-lock.json
include:
- src/api.js |
|
Миграция существующих Node.js приложений в бессерверную архитектуру
Перенос существующего приложения в бессерверную среду часто представляет сложности. Вот пример стратегии миграции монолитного Express.js приложения в Lambda с API Gateway:
1. Анализ и разделение приложения
Вместо прямой миграции всего монолита, лучше разбить его на функциональные модули. Для маршрутизации запросов можно использовать AWS API Gateway или интеграцию с express через serverless-http:
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| const serverless = require('serverless-http');
const express = require('express');
const app = express();
// Определяем маршруты
app.get('/users', async (req, res) => {
// Логика получения пользователей
res.json({ users: [...] });
});
app.post('/users', async (req, res) => {
// Логика создания пользователя
res.json({ success: true, userId: '123' });
});
// Экспортируем обработчик для Lambda
module.exports.handler = serverless(app); |
|
2. Адаптация работы с состоянием
В бессерверной среде нельзя полагаться на локальное хранение состояния. Необходимо перенести все состояние в внешние хранилища:
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| // Было в монолите
const sessions = {};
app.get('/login', (req, res) => {
const sessionId = generateId();
sessions[sessionId] = { userId: req.body.userId };
res.cookie('sessionId', sessionId);
res.redirect('/dashboard');
});
// Стало в бессерверной версии
const AWS = require('aws-sdk');
const dynamoDB = new AWS.DynamoDB.DocumentClient();
app.get('/login', async (req, res) => {
const sessionId = generateId();
await dynamoDB.put({
TableName: 'Sessions',
Item: {
sessionId,
userId: req.body.userId,
expiresAt: Math.floor(Date.now() / 1000) + 86400 // 24 часа
}
}).promise();
res.cookie('sessionId', sessionId);
res.redirect('/dashboard');
}); |
|
3. Постепенная миграция
Лучшая стратегия — постепенно переносить функциональность, начиная с наименее критичных компонентов и двигаясь к более важным после подтверждения работоспособности подхода.
Разработка скриптов на JavaScript! 1. Выделить из файла со списком файлов строки с именами htm-файлов и создать htm-
файл со ссылками
2. Создать N каталогов, в каждый из которых... ArcGis JavaScript API Здравствуйте. Появилась задача Сделать кадастровую карту. Решил использовать api от arcgis. Нужно подключить wms слой... Yandex API и JavaScript Добрый вечер. Помогите смастерить такую штуку.. есть вот этот скрипт..
<script type="text/javascript">
// Константы.
var... ArcGis JavaScript API Здравствуйте. Появилась задача Сделать кадастровую карту. Решил использовать api от arcgis. Нужно подключить wms слой... Разработка прилложения по API вконтакте Скажите, пожалуйста, можно ли на сайте с помощью php или javascript организовать рассылку по стенам пользователей вконтакте? Нужен ли token? Или... Разработка клиентских приложений на JavaScript Сегодня листал вакансии, нашел очень даже годную вакансию, все что мне нравится.
Есдинственное что там было сказано, что требуется умение... JavaScript API для Office здравствуйте кто-то в Ворде применял? в Ворде какого года это будет работать? поделитесь кусками кода-примерами, спасибо Vk.com API и javaScript, немогу разобраться Доброго времени суток!
Кто знает как для vk писать скрипт, делаю все по инструкции
но ничего не выходит:
1. Подключение в приложении ... Google Maps Javascript API v3 Я вставил Карту гугл на сайт. Как сделать так, чтоб она сразу находила местоположение пользователя?
Добавлено через 56 секунд
<!DOCTYPE... Сконструировать \lambda-вызов и вычислить его значение Lambda Задание
Для выражения из таблицы согласно номеру варианта сконструировать \lambda - вызов и вычислить его значение. Присваивать полученное... Построение масштабируемых графиков Здравствуйте форумчане, требуется написать приложение на Windows Forms C# для построения масштабируемых графиков. Прошу, подскажите, какие ресурсы... Telegraph API форма запроса (java, javascript, API, Telegraph) Добрый день!
Как сформировать запрос к Telegraph API с отправкой ссылки на видео?
...
|