Статьи

Amazon AWS Lambda Tutorial — Руководство ULTIMATE (Скачать PDF)

ПРИМЕЧАНИЕ ДЛЯ РЕДАКЦИИ: AWS Lambda — это управляемая событиями, серверная вычислительная платформа, предоставляемая Amazon в составе веб-сервисов Amazon. Это вычислительная служба, которая запускает код в ответ на события и автоматически управляет вычислительными ресурсами, необходимыми для этого кода.

Цель Lambda по сравнению с AWS EC2 — упростить создание небольших приложений по требованию, которые реагируют на события и новую информацию. AWS предназначается для запуска экземпляра Lambda в течение миллисекунд после события. Node.js, Python, Java, Go и C # через .NET Core официально поддерживаются с 2016 года, а другие языки могут поддерживаться с помощью обратных вызовов. Однако некоторые среды выполнения, такие как виртуальная машина Java, могут запускаться медленнее, чем другие.

AWS Lambda была разработана для случаев использования, таких как загрузка изображений или объектов в Amazon S3, обновление таблиц DynamoDB, реагирование на щелчки веб-сайтов или реагирование на показания датчиков с устройства, подключенного к IoT. AWS Lambda также можно использовать для автоматического предоставления внутренних служб, запускаемых по пользовательским HTTP-запросам, и «раскручивать» такие службы, когда они не используются, для экономии ресурсов. (Источник: Википедия )

Теперь мы предоставляем исчерпывающее руководство, чтобы вы могли разрабатывать свои собственные приложения на основе Amazon Lambda. Мы охватываем широкий спектр тем, чтобы иметь возможность запускать и запускать собственные проекты в минимальные сроки. Наслаждайтесь!

1. Введение

Amazon Web Services (AWS) предоставляет инфраструктуру для размещения и запуска ваших приложений на разных уровнях абстракции.

Самым очевидным решением для размещения кода, конечно же, является аренда машины без поддержки, настройка операционной системы и установка всего необходимого программного обеспечения. Вы можете сделать это с Amazon EC2, хотя этот сервис больше известен арендой виртуальных машин, работающих на физических хостах, предоставляемых Amazon. В обоих решениях вы должны заботиться об используемой операционной системе и установке серверного программного обеспечения (например, Tomcat).

Amazon Beanstalk делает еще один шаг вперед, предоставляя сервисы, которые принимают готовый к использованию файл войны и размещают его на виртуальной машине, настроенной Amazon от вашего имени. Как разработчику, вам больше не нужно заботиться о деталях настройки операционной системы или серверного программного обеспечения. Если вы реализуете свое приложение таким образом, чтобы оно не зависело от операционной системы и работало на предоставленной версии Apache Tomcat, Amazon может автоматически развернуть ваше программное обеспечение и даже масштабировать количество виртуальных машин, необходимое для обслуживания всех клиентов без особых задержек. Вы просто указываете Amazon ограничения, в которых он должен работать, и можете сосредоточиться на реализации.

С Amazon Lambda уровень абстракции поднимается еще на один шаг выше. С Lambda вы больше не заботитесь о виртуальных машинах и их масштабировании самостоятельно. Все, что вам нужно сделать, это предоставить Amazon некоторый код (обычно в форме подготовленного jar-файла), и Amazon заботится о его выполнении от вашего имени. В отличие от Amazon Beanstalk, вас не заботит ни количество виртуальных машин, используемых для выполнения, ни количество балансировщиков нагрузки. Amazon обещает выполнять ваш код так часто, как это требуется. И вы платите только за время, потраченное на его выполнение, а не за время работы ваших виртуальных машин. Последний пункт чрезвычайно интересен для операций, которые не выполняются на регулярной основе, или если вы только начинаете новый бизнес. Если вам не нужен ваш сервис, вы ничего не платите. С другой стороны, если ваш бизнес начинается и у вас за ночь появляются сотни новых клиентов, вам не нужно заботиться об аренде новых серверов или об изменении лимитов, в которых будет работать Amazon.

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

Вы можете вызывать лямбда-функции в ответ на различные события. Когда конкретный сегмент S3 изменяется или данные вставляются или удаляются из таблицы DynamoDB. Вы можете отобразить HTTP-запросы, поступающие из Интернета, к определенной функции Lambda, используя шлюз API. Или вы просто вызываете код, используя вызовы API из AWS SDK. Таким образом, лямбда-функции могут использоваться как триггеры в классических системах баз данных или могут использоваться для создания законченных приложений.

Код для Amazon Lambda может быть написан на разных языках: Node.js, Java, Go, C # и Python. В этом руководстве мы сосредоточимся, конечно, на Java, но для небольших функций вы можете рассмотреть язык сценариев, который не нужно компилировать и предоставлять в определенном формате архива.

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

2. Концепции

Следующие концепции составляют Amazon Lambda:

  • Лямбда-функция: это пользовательский код, который вы написали и который будет выполняться инфраструктурой Amazon.
  • Источник события: служба, которая запускает выполнение вашей лямбда-функции.
  • Нисходящие ресурсы: другие сервисы, которые вызываются вашим кодом.
  • Потоки журналов. Ваш код может создавать выходные данные журналов, собираемые инфраструктурой Amazon.

Функция Lambda — это обычный метод Java, выполняемый инфраструктурой Amazon. Когда вы загружаете свой код (то есть файл jar), вы можете указать, какой метод Java связан с конкретной функцией Lambda. Инфраструктура передает любые данные о событиях в функцию в качестве первого параметра. Ваша функция может оценивать эти данные и вызывать любую дальнейшую логику.

Второй параметр вашей лямбда-функции — это объект контекста. Его можно использовать для взаимодействия со средой выполнения AWS. Например, вы можете запросить оставшееся время выполнения до завершения вашей функции.

Кроме того, вы можете использовать каркас журналирования, такой как log4j, для создания операторов журналов, которые отправляются в Amazon CloudWatch. Исключение может использоваться для прекращения выполнения функции и отправляется клиенту в зависимости от способа вызова функции.

3. Использование

В этой главе объясняется, как реализовать функции Lambda, и описываются различные аспекты, связанные с инфраструктурой Lambda.

3.1 Ваша первая функция

Теперь, когда мы узнали об основных понятиях Amazon Lambda, мы можем приступить к реализации нашей первой функции.

В этом уроке мы будем использовать maven в качестве системы сборки. Если вы не знакомы с maven, вы можете найти инструкции по настройке и использованию maven, например, здесь .

Чтобы создать простой проект maven, мы вводим следующую команду в командной строке:

1
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">mvn archetype:generate -DgroupId=com.javacodegeeks.com -DartifactId=aws-lambda -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false</span> mvn архетип: генерировать -DgroupId = com.javacodegeeks.com -DartifactId = aws-lambda -DarchetypeArtifactId = maven-archetype-quickstart -DinteractiveMode = false</span>

Это создает новый каталог с именем aws-lambda в нашем текущем рабочем каталоге со следующей структурой:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">aws-lambda</span> AWS-лямбда</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">|-- pom.xml</span> | - pom.xml</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">`-- src</span> `- SRC</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">|-- main</span> | - главная</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">|</span> |</span> <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">`-- java</span> `- Ява</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">|</span> |</span> <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">`-- com</span> `- ком</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">|</span> |</span> <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">`-- javacodegeeks</span> `- javacodegeeks</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">|</span> |</span> <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">`-- aws</span> `- AWS</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">|</span> |</span> <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">`-- App.java</span> `- App.java</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">`-- test</span> `- тест</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">`-- java</span> `- Ява</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">`-- com</span> `- ком</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">`-- javacodegeeks</span> `- javacodegeeks</span>
                    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">`-- aws</span> `- AWS</span>
                        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">`-- AppTest.java</span> `- AppTest.java</span>

Вы можете удалить два файла App.java и AppTest.java как мы не собираемся создавать нормальное Java-приложение.

Чтобы получить доступ к лямбда-классам AWS, мы добавляем следующую зависимость maven в pom.xml :

1
2
3
4
5
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><dependency></span> <Зависимость></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><groupId>com.amazonaws</groupId></span> <Идентификатор_группы> com.amazonaws </ идентификатор_группы></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><artifactId>aws-lambda-java-core</artifactId></span> <Артефакт> AWS-лямбда-ява-ядро </ артефакт></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><version>1.2.0</version></span> <Версия> 1.2.0 </ версия></span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"></dependency></span> </ Зависимость></span>

Дополнительно мы добавляем следующий плагин в раздел plugins вашей build :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><plugin></span> <Плагин></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><groupId>org.apache.maven.plugins</groupId></span> <Идентификатор_группы> org.apache.maven.plugins </ идентификатор_группы></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><artifactId>maven-shade-plugin</artifactId></span> <Артефакт> Maven-тень-плагин </ артефакт></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><version>2.4.3</version></span> <Версия> 2.4.3 </ версия></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><configuration></span> <Конфигурация></span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><createDependencyReducedPom>false</createDependencyReducedPom></span> <CreateDependencyReducedPom> ложь </ createDependencyReducedPom></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"></configuration></span> </ Конфигурация></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><executions></span> <казни></span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><execution></span> <Исполнение></span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><phase>package</phase></span> <Фаза> пакет </ фаза></span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><goals></span> <цели></span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><goal>shade</goal></span> <Цель> оттенок </ цель></span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"></goals></span> </ Цели></span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"></execution></span> </ Выполнение></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"></executions></span> </ Расстрелы></span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"></plugin></span> </ Плагин></span>

Плагины Shade создают Uber JAR, который содержит все зависимости. Таким образом, мы можем загрузить одну банку вместо предоставления всех зависимостей отдельно.

Существуют разные способы реализации обработчика запросов в Lambda, здесь мы выбрали тот, который реализует интерфейс RequestHandler<T,U> :

01
02
03
04
05
06
07
08
09
10
11
12
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">package com.javacodegeeks.aws;</span> пакет com.javacodegeeks.aws;</span><font></font>
<font></font>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import com.amazonaws.services.lambda.runtime.Context;</span> import com.amazonaws.services.lambda.runtime.Context;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import com.amazonaws.services.lambda.runtime.RequestHandler;</span> import com.amazonaws.services.lambda.runtime.RequestHandler;</span><font></font>
<font></font>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">public class HelloWorldRequestHandler implements RequestHandler<String,String> {</span> открытый класс HelloWorldRequestHandler реализует RequestHandler <String, String> {</span><font></font>
<font></font>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">@Override</span> @Override</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">public String handleRequest(String s, Context context) {</span> public String handleRequest (String s, Контекстный контекст) {</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">return "My first request handler: " + s;</span> return "Мой первый обработчик запросов:" + s;</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>

Два общих параметра интерфейса RequestHandler определяют типы первого аргумента и возвращаемое значение метода handleRequest . Этот простой пример просто использует java.lang.String , но вы можете использовать здесь произвольные классы. Источник события преобразует входящие данные (например, JSON внутри HTTP-запроса) в объект типа запроса.

Теперь вы можете собрать jar, выполнив следующую команду:

1
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">mvn package</span> мвн пакет</span>

После этого вы найдете два разных jar-файла в target директории:

1
2
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">aws-lambda-1.0-SNAPSHOT.jar</span> AWS-лямбда-1,0-SNAPSHOT.jar</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">original-aws-lambda-1.0-SNAPSHOT.jar</span> оригинальные-AWS лямбда-1,0-SNAPSHOT.jar</span>

Файл jar без префикса original представляет собой затененный файл jar, содержащий зависимости. Вы можете легко проверить это, открыв банку с программой просмотра zip по вашему выбору и перечислив классы. Вы увидите классы из пакета com.amazonaws .

Теперь вам нужно войти в консоль AWS. Если вы впервые используете AWS, вам необходимо создать учетную запись AWS. Как это сделать, описано, например, здесь .

Amazon Lambda - создать функцию

Рис. 1: Функции

Внутри консоли AWS вы можете выбрать «Лямбда» в меню «Услуги». На странице перечислены все доступные лямбда-функции и есть кнопка для создания новой функции:

После нажатия кнопки «Создать функцию» на следующей странице мы можем предоставить подробную информацию о ней:

Amazon Lambda - создать функцию

Рис. 2: создание функции

Поскольку мы реализовали функцию полностью с нуля, мы выбрали опцию «Автор с нуля». Кроме того, вы также можете выбрать предварительно настроенный шаблон или найти реализацию в хранилище Amazon.

Окно «Автор с нуля» просит нас ввести имя, выбрать среду выполнения (здесь: Java 8) и роль.

Если это ваша первая лямбда-функция, вы можете создать новую роль, выбрав «Создать пользовательскую роль». Это откроет новые окна браузера, в которых вас попросят подробнее узнать о новой роли:

Amazon лямбда - createRole

Рис. 3: createRole

В этом диалоговом окне мы выбрали «Создать новую роль IAM» и установили «lambda_basic_execution» в качестве имени роли. Как только мы нажмем «Разрешить», роль будет выбрана в поле «Автор с нуля».

Нажатие «Создать функцию» открывает следующую страницу:

Amazon Лямбда-загрузкаФункция

Рис. 4: функция загрузки

Внутри поля «Код функции» мы выбрали «Загрузить .ZIP или JAR» в качестве «Типа ввода кода» и в качестве среды выполнения «Java 8». Поле ввода «Обработчик» позволяет нам указать имя вызываемой функции в форме Class::method . В нашем примере сверху мы вводим:

1
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">com.javacodegeeks.aws.HelloWorldRequestHandler::handleRequest</span> com.javacodegeeks.aws.HelloWorldRequestHandler :: handleRequest</span>

Кнопка «Загрузить» вызывает диалог выбора файла, где вы можете выбрать файл JAR для загрузки. В нашем примере мы выбрали файл aws-lambda-1.0-SNAPSHOT.jar .

Теперь мы можем нажать «Сохранить». Это позволяет Amazon Lambda создать функцию. Теперь он готов к исполнению.

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

Amazon Lambda testEvent

Рис. 5: testEvent

Мы выбрали опцию «Создать новое тестовое событие», предоставили имя для события и задали постоянную строку «JavaCodeGeeks» в качестве строки. Это строка, которая предоставляется в качестве первого параметра нашей функции. После этого мы можем нажать «Тест», и тест будет выполнен:

Amazon лямбда-тестРезультат

Рис. 6: testResult

Внутри окна «Результат выполнения» вы можете увидеть строку My first request handler: JavaCodeGeeks . Это именно то возвращаемое значение, которое мы ожидали получить с помощью нашей функции выборки.

В разделе сводки вы также можете найти информацию о расчетной продолжительности (здесь: 100 мс), максимальном объеме используемой памяти (здесь: 42 МБ) и фактической продолжительности (здесь: 34,89 мс).

На Лямбда-панели вы найдете некоторые метрики о вызовах ваших функций. После нашего первого теста они могут выглядеть так:

Amazon Лямбда метрики

Рис. 7: метрика

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

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

Функция Amazon LambdaНастройки

Рис. 8: настройки функций

Поле «Переменные среды» позволяет вам определить набор переменных, к которым можно обращаться во время выполнения. Это полезно для простых опций конфигурации.

Если у вас много лямбда-функций, вы можете отфильтровать их по тегам. Следовательно, можно предоставить пары ключ / значение для каждой функции.

Ранее мы узнали, что лямбда-функция выполняется в контексте определенной роли пользователя. Эта роль может быть указана в поле «Роль выполнения». Роль определяет набор разрешений, которые могут быть предоставлены пользователям или, как в этом примере, функциям Lambda. Чтобы иметь возможность вызывать лямбда-функцию, вы должны определить роль, которая имеет разрешение CloudWatch Logs.

Если вы не уверены, что у выбранной роли есть необходимые разрешения, вы можете перейти к службе IAM в консоли AWS и просмотреть страницу сведений о роли:

Амазонка лямбда лямбдаРоле

Рис. 9: лямбда-роль

В нижней таблице мы видим, что эта конкретная роль имеет разрешение на запись в сервис Amazon CloudWatch.

Кроме того, вы можете настроить объем памяти, который инфраструктура должна предоставить для вашей функции, а также интервал времени ожидания, после которого ваша функция будет прекращена. Оба параметра позволяют вам контролировать расходы, поскольку вы платите за общее время, используемое вашими функциями, а также за память, предоставленную для них. Если вы создали новую учетную запись AWS, вы можете воспользоваться предложением бесплатного уровня, что означает, что в течение первых 12 месяцев вы получаете 1 миллион запросов и 400 000 ГБ-секунд бесплатно.

Рядом с настройками, описанными выше, вы можете ограничить параллелизм вашей функции (по умолчанию — неограниченный параллелизм), выбрать виртуальную частную сеть, в которой работает ваша функция, и выбрать ресурс, такой как сервис SNS Amazon, в который отправляются события, если определенное количество обработка через лямбда-функции не удалась.

3.2 Реализации обработчиков

В предыдущем разделе мы увидели, как реализовать простой обработчик. Но мы не ограничены этим конкретным форматом.

Вместо реализации интерфейса RequestHandler мы также можем использовать простой Java-класс, который не реализует никаких конкретных интерфейсов. Следующий класс сделает то же самое:

01
02
03
04
05
06
07
08
09
10
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">package com.javacodegeeks.aws;</span> пакет com.javacodegeeks.aws;</span><font></font>
<font></font>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import com.amazonaws.services.lambda.runtime.Context;</span> import com.amazonaws.services.lambda.runtime.Context;</span><font></font>
<font></font>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">public class HelloWorldHandler {</span> открытый класс HelloWorldHandler {</span><font></font>
<font></font>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">public String handleRequest(String input, Context context) {</span> public String handleRequest (ввод строки, контекстный контекст) {</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">return "My first request handler: " + input;</span> return "Мой первый обработчик запросов:" + input;</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>

RequestHandler интерфейса RequestHandler Lambda SDK также предоставляет интерфейс RequestStreamHandler . Этот интерфейс обеспечивает реализацию метода, который использует InputStream для ввода и OutputStream качестве возвращаемого значения:

Чтобы преобразовать InputStream в String , мы используем библиотеку Apache commons-io:

1
2
3
4
5
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><dependency></span> <Зависимость></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><groupId>commons-io</groupId></span> <GroupID> Обще-ю </ идентификатор_группы></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><artifactId>commons-io</artifactId></span> <Артефакт> Обще--й </ артефакт></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><version>2.5</version></span> <Версия> 2.5 </ версия></span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"></dependency></span> </ Зависимость></span>

Теперь мы можем реализовать ту же логику, что и выше:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">package com.javacodegeeks.aws;</span> пакет com.javacodegeeks.aws;</span><font></font>
<font></font>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import com.amazonaws.services.lambda.runtime.Context;</span> import com.amazonaws.services.lambda.runtime.Context;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import com.amazonaws.services.lambda.runtime.RequestStreamHandler;</span> import com.amazonaws.services.lambda.runtime.RequestStreamHandler;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import org.apache.commons.io.IOUtils;</span> import org.apache.commons.io.IOUtils;</span><font></font>
<font></font>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import java.io.IOException;</span> импорт java.io.IOException;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import java.io.InputStream;</span> import java.io.InputStream;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import java.io.OutputStream;</span> import java.io.OutputStream;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import java.nio.charset.Charset;</span> импорт java.nio.charset.Charset;</span><font></font>
<font></font>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">public class HelloWorldRequestStreamHandler implements RequestStreamHandler {</span> открытый класс HelloWorldRequestStreamHandler реализует RequestStreamHandler {</span><font></font>
<font></font>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">@Override</span> @Override</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">public void handleRequest(InputStream inputStream, OutputStream outputStream,</span> public void handleRequest (InputStream inputStream, OutputStream outputStream,</span>
                              <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">Context context) throws IOException {</span> Контекстный контекст) выдает IOException {</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">Charset encoding = Charset.forName("UTF-8");</span> Кодировка Charset = Charset.forName ("UTF-8");</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">String input = IOUtils.toString(inputStream, encoding);</span> String input = IOUtils.toString (inputStream, encoding);</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">String output = "My first request handler: " + input;</span> String output = "Мой первый обработчик запросов:" + input;</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">IOUtils.write(output, outputStream, encoding);</span> IOUtils.write (output, outputStream, encoding);</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>

3.3 Контекст

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

Вы можете использовать объект Context для взаимодействия со средой выполнения вашей функции Lambda. Следовательно, Context предоставляет ряд свойств, к которым может обращаться ваша функция. Следующая лямбда-функция показывает, как вернуть некоторую интересную информацию из Context :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">package com.javacodegeeks.aws;</span> пакет com.javacodegeeks.aws;</span><font></font>
<font></font>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import com.amazonaws.services.lambda.runtime.ClientContext;</span> import com.amazonaws.services.lambda.runtime.ClientContext;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import com.amazonaws.services.lambda.runtime.Context;</span> import com.amazonaws.services.lambda.runtime.Context;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import com.amazonaws.services.lambda.runtime.RequestHandler;</span> import com.amazonaws.services.lambda.runtime.RequestHandler;</span><font></font>
<font></font>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import java.util.Map;</span> импорт java.util.Map;</span><font></font>
<font></font>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">public class ContextHandler implements RequestHandler<String,String> {</span> открытый класс ContextHandler реализует RequestHandler <String, String> {</span><font></font>
<font></font>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">@Override</span> @Override</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">public String handleRequest(String input, Context context) {</span> public String handleRequest (ввод строки, контекстный контекст) {</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">StringBuilder sb = new StringBuilder();</span> StringBuilder sb = new StringBuilder ();</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">sb.append("awsRequestId=").append(context.getAwsRequestId()).append("\n");</span> . Sb.append ( "awsRequestId =") Append (context.getAwsRequestId ()) Append ( "\ п.");</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">sb.append("functionName=").append(context.getFunctionName()).append("\n");</span> .. Sb.append ( "FunctionName =") Append (context.getFunctionName ()) Append ( "\ п");</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">sb.append("functionVersion=").append(context.getFunctionVersion()).append("\n");</span> .. Sb.append ( "functionVersion =") Append (context.getFunctionVersion ()) Append ( "\ п");</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">sb.append("memoryLimitInMB=").append(context.getMemoryLimitInMB()).append("\n");</span> . Sb.append ( "memoryLimitInMB =") Append (context.getMemoryLimitInMB ()) Append ( "\ п.");</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">sb.append("remainingTimeInMillis=").append(context.getRemainingTimeInMillis()).append("\n");</span> .. Sb.append ( "remainingTimeInMillis =") Append (context.getRemainingTimeInMillis ()) Append ( "\ п");</span><font></font>
<font></font>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">return sb.toString();</span> return sb.toString ();</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>

В приведенном выше примере мы возвращаем следующие свойства:

  • awsRequestId: это идентификатор, связанный с текущим запросом. Этот идентификатор остается прежним при повторных попытках.
  • functionName: имя лямбда-функции, как определено в AWS.
  • functionVersion: версия функции.
  • memoryLimitInMB: ограничение памяти, которое мы установили для этой функции.
  • Остальная часть времени: количество миллисекунд, оставшихся до завершения выполнения.

Когда мы загружаем приведенный выше класс и настраиваем его как лямбда-функцию «returnContext», мы получаем следующий пример вывода:

1
2
3
4
5
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">awsRequestId=89566fcc-6e37-11e8-805a-e97b7daee95b</span> awsRequestId = 89566fcc-6e37-11e8-805a-e97b7daee95b</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">functionName=returnContext</span> FunctionName = returnContext</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">functionVersion=$LATEST</span> functionVersion = $ Последняя</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">memoryLimitInMB=256</span> memoryLimitInMB = 256</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">remainingTimeInMillis=14950</span> remainingTimeInMillis = 14950</span>

Обратите внимание, что мы сократили лимит памяти до 256 ГБ, что отлично отражается в примере вывода.

3.4 Ведение журнала

Как упоминалось ранее, вы можете использовать операторы журнала для отслеживания выполнения вашего кода. Эти строки журнала отправляются в Amazon CloudWatch и сохраняются там для последующего анализа.

Простейшая форма регистрации — использовать System.out :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">package com.javacodegeeks.aws;</span> пакет com.javacodegeeks.aws;</span><font></font>
<font></font>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import com.amazonaws.services.lambda.runtime.Context;</span> import com.amazonaws.services.lambda.runtime.Context;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import com.amazonaws.services.lambda.runtime.RequestHandler;</span> import com.amazonaws.services.lambda.runtime.RequestHandler;</span><font></font>
<font></font>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">public class SysOutContextHandler implements RequestHandler<String,String> {</span> открытый класс SysOutContextHandler реализует RequestHandler <String, String> {</span><font></font>
<font></font>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">@Override</span> @Override</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">public String handleRequest(String input, Context context) {</span> public String handleRequest (ввод строки, контекстный контекст) {</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">System.out.println("awsRequestId=" + context.getAwsRequestId());</span> System.out.println ("awsRequestId =" + context.getAwsRequestId ());</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">System.out.println("functionName=" + context.getFunctionName());</span> System.out.println ("functionName =" + context.getFunctionName ());</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">System.out.println("functionVersion=" + context.getFunctionVersion());</span> System.out.println ("functionVersion =" + context.getFunctionVersion ());</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">System.out.println("memoryLimitInMB=" + context.getMemoryLimitInMB());</span> System.out.println ("memoryLimitInMB =" + context.getMemoryLimitInMB ());</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">System.out.println("remainingTimeInMillis=" + context.getRemainingTimeInMillis());</span> System.out.println ("ОстальноеTimeInMillis =" + context.getRemainingTimeInMillis ());</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">return "OK";</span> вернуть «ОК»;</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>

Запуск этой функции с именем «logContext» создаст следующие события журнала в CloudWatch:

Amazon Лямбда cloudWatchLogs

Рис. 10: cloudWatchLogs

Вы можете перейти к CloudWatch, нажав на синюю ссылку «logs» после выполнения функции. Как мы видим, Amazon создал одно событие журнала для начала функции, по одному на каждый вызов System.out.println() и два в конце выполнения.

Обычно Amazon Lambda создает одно событие журнала для каждого вызова System.out.println() . Если строка содержит разрыв строки, каждая строка будет рассматриваться как отдельное событие журнала.

Вместо использования System.out и System.err вы также можете использовать регистратор, предоставляемый объектом Context :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">package com.javacodegeeks.aws;</span> пакет com.javacodegeeks.aws;</span><font></font>
<font></font>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import com.amazonaws.services.lambda.runtime.Context;</span> import com.amazonaws.services.lambda.runtime.Context;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import com.amazonaws.services.lambda.runtime.LambdaLogger;</span> import com.amazonaws.services.lambda.runtime.LambdaLogger;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import com.amazonaws.services.lambda.runtime.RequestHandler;</span> import com.amazonaws.services.lambda.runtime.RequestHandler;</span><font></font>
<font></font>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">public class LambdaLoggerContextHandler implements RequestHandler<String,String> {</span> открытый класс LambdaLoggerContextHandler реализует RequestHandler <String, String> {</span><font></font>
<font></font>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">@Override</span> @Override</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">public String handleRequest(String input, Context context) {</span> public String handleRequest (ввод строки, контекстный контекст) {</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">LambdaLogger logger = context.getLogger();</span> LambdaLogger logger = context.getLogger ();</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">logger.log("awsRequestId=" + context.getAwsRequestId());</span> logger.log ("awsRequestId =" + context.getAwsRequestId ());</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">logger.log("functionName=" + context.getFunctionName());</span> logger.log ("functionName =" + context.getFunctionName ());</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">logger.log("functionVersion=" + context.getFunctionVersion());</span> logger.log ("functionVersion =" + context.getFunctionVersion ());</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">logger.log("memoryLimitInMB=" + context.getMemoryLimitInMB());</span> logger.log ("memoryLimitInMB =" + context.getMemoryLimitInMB ());</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">logger.log("remainingTimeInMillis=" + context.getRemainingTimeInMillis());</span> logger.log ("Остаток ТаймИнМиллис =" + context.getRemainingTimeInMillis ());</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">return "OK";</span> вернуть «ОК»;</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>

Это, возможно, самый простой способ, но это также означает, что ваш код привязан к платформе AWS.

Чтобы оставаться более независимым от среды выполнения, вы также можете использовать популярную библиотеку журналов log4j.

Чтобы использовать log4j, вы должны включить следующие зависимости maven:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><dependency></span> <Зависимость></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><groupId>com.amazonaws</groupId></span> <Идентификатор_группы> com.amazonaws </ идентификатор_группы></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><artifactId>aws-lambda-java-log4j2</artifactId></span> <Артефакт> AWS лямбда-ява-log4j2 </ артефакт></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><version>1.0.0</version></span> <Версия> 1.0.0 </ версия></span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"></dependency></span> </ Зависимость></span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><dependency></span> <Зависимость></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><groupId>org.apache.logging.log4j</groupId></span> <Идентификатор_группы> org.apache.logging.log4j </ идентификатор_группы></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><artifactId>log4j-core</artifactId></span> <Артефакт> log4j-ядро </ артефакт></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><version>2.8.2</version></span> <Версия> 2.8.2 </ версия></span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"></dependency></span> </ Зависимость></span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><dependency></span> <Зависимость></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><groupId>org.apache.logging.log4j</groupId></span> <Идентификатор_группы> org.apache.logging.log4j </ идентификатор_группы></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><artifactId>log4j-api</artifactId></span> <Артефакт> log4j-апите </ артефакт></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><version>2.8.2</version></span> <Версия> 2.8.2 </ версия></span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"></dependency></span> </ Зависимость></span>

Кроме того, плагин shade должен быть настроен на использование log4j2-cachefile-transfomer:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><plugin></span> <Плагин></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><groupId>org.apache.maven.plugins</groupId></span> <Идентификатор_группы> org.apache.maven.plugins </ идентификатор_группы></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><artifactId>maven-shade-plugin</artifactId></span> <Артефакт> Maven-тень-плагин </ артефакт></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><version>2.4.3</version></span> <Версия> 2.4.3 </ версия></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><configuration></span> <Конфигурация></span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><createDependencyReducedPom>false</createDependencyReducedPom></span> <CreateDependencyReducedPom> ложь </ createDependencyReducedPom></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"></configuration></span> </ Конфигурация></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><executions></span> <казни></span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><execution></span> <Исполнение></span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><phase>package</phase></span> <Фаза> пакет </ фаза></span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><goals></span> <цели></span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><goal>shade</goal></span> <Цель> оттенок </ цель></span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"></goals></span> </ Цели></span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><configuration></span> <Конфигурация></span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><transformers></span> <Трансформаторы></span>
                    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><transformer</span> <трансформатор</span>
                            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">implementation="com.github.edwgiz.mavenShadePlugin.log4j2CacheTransformer.PluginsCacheFileTransformer"></span> реализация = "com.github.edwgiz.mavenShadePlugin.log4j2CacheTransformer.PluginsCacheFileTransformer"></span>
                    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"></transformer></span> </ Трансформатор></span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"></transformers></span> </ Трансформаторы></span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"></configuration></span> </ Конфигурация></span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"></execution></span> </ Выполнение></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"></executions></span> </ Расстрелы></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><dependencies></span> <Зависимостей></span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><dependency></span> <Зависимость></span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><groupId>com.github.edwgiz</groupId></span> <Идентификатор_группы> com.github.edwgiz </ идентификатор_группы></span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><artifactId>maven-shade-plugin.log4j2-cachefile-transformer</artifactId></span> <Артефакт> Maven-тень-plugin.log4j2-cachefile-трансформатор </ артефакт></span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><version>2.8.1</version></span> <Версия> 2.8.1 </ версия></span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"></dependency></span> </ Зависимость></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"></dependencies></span> </ Зависимости></span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"></plugin></span> </ Плагин></span>

Библиотеку log4j можно настроить, поместив файл с именем log4j2.xml в log4j2.xml src/main/resources проекта:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><?xml version="1.0" encoding="UTF-8"?></span> <? xml version = "1.0" encoding = "UTF-8"?></span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><Configuration packages="com.amazonaws.services.lambda.runtime.log4j2"></span> <Configuration packages = "com.amazonaws.services.lambda.runtime.log4j2"></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><Appenders></span> <Appenders></span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><Lambda name="Lambda"></span> <Lambda name = "Lambda"></span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><PatternLayout></span> <PatternLayout></span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><pattern>%d{yyyy-MM-dd HH:mm:ss} %X{AWSRequestId} %-5p %c{1}:%L - %m%n</pattern></span> <pattern>% d {гггг-мм-дд ЧЧ: мм: сс}% X {AWSRequestId}% -5p% c {1}:% L -% m% n </ pattern></span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"></PatternLayout></span> </ PatternLayout></span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"></Lambda></span> </ Lambda></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"></Appenders></span> </ Appenders></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><Loggers></span> <Лесорубы></span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><Root level="info"></span> <Root level = "info"></span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><AppenderRef ref="Lambda" /></span> <AppenderRef ref = "Lambda" /></span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"></Root></span> </ Root></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"></Loggers></span> </ Лесорубы></span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"></Configuration></span> </ Конфигурация></span>

Теперь мы можем увидеть следующие события журнала в CloudWatch:

1
2
3
4
5
6
7
8
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">START RequestId: 5cfc1f27-6e3c-11e8-bbb9-c79d86ee7e9f Version: $LATEST</span> START RequestId: 5cfc1f27-6e3c-11e8-bbb9-c79d86ee7e9f Версия: $ LATEST</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">2018-06-16 12:30:18 5cfc1f27-6e3c-11e8-bbb9-c79d86ee7e9f INFO Log4j2ContextHandler:13 - awsRequestId=5cfc1f27-6e3c-11e8-bbb9-c79d86ee7e9f</span> 2018-06-16 12:30:18 5cfc1f27-6e3c-11e8-bbb9-c79d86ee7e9f INFO Log4j2ContextHandler: 13 - awsRequestId = 5cfc1f27-6e3c-11e8-bbb9-c79d86ee7e9f</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">2018-06-16 12:30:18 5cfc1f27-6e3c-11e8-bbb9-c79d86ee7e9f INFO Log4j2ContextHandler:14 - functionName=logContext</span> 2018-06-16 12:30:18 5cfc1f27-6e3c-11e8-bbb9-c79d86ee7e9f ИНФОРМАЦИЯ Log4j2ContextHandler: 14 - functionName = logContext</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">2018-06-16 12:30:18 5cfc1f27-6e3c-11e8-bbb9-c79d86ee7e9f INFO Log4j2ContextHandler:15 - functionVersion=$LATEST</span> 2018-06-16 12:30:18 5cfc1f27-6e3c-11e8-bbb9-c79d86ee7e9f ИНФОРМАЦИЯ Log4j2ContextHandler: 15 - functionVersion = $ LATEST</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">2018-06-16 12:30:18 5cfc1f27-6e3c-11e8-bbb9-c79d86ee7e9f INFO Log4j2ContextHandler:16 - memoryLimitInMB=512</span> 2018-06-16 12:30:18 5cfc1f27-6e3c-11e8-bbb9-c79d86ee7e9f INFO Log4j2ContextHandler: 16 - memoryLimitInMB = 512</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">2018-06-16 12:30:18 5cfc1f27-6e3c-11e8-bbb9-c79d86ee7e9f INFO Log4j2ContextHandler:17 - remainingTimeInMillis=14910</span> 2018-06-16 12:30:18 5cfc1f27-6e3c-11e8-bbb9-c79d86ee7e9f ИНФОРМАЦИЯ Log4j2ContextHandler: 17 - ОстатокTimeInMillis = 14910</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">END RequestId: 5cfc1f27-6e3c-11e8-bbb9-c79d86ee7e9f</span> КОНЕЦ RequestId: 5cfc1f27-6e3c-11e8-bbb9-c79d86ee7e9f</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">REPORT RequestId: 5cfc1f27-6e3c-11e8-bbb9-c79d86ee7e9f Duration: 110.00 ms Billed Duration: 200 ms Memory Size: 512 MB Max Memory Used: 73 MB</span> REPORT RequestId: 5cfc1f27-6e3c-11e8-bbb9-c79d86ee7e9f Продолжительность: 110,00 мс Продолжительность счета: 200 мс Размер памяти: 512 МБ Макс. Используемая память: 73 МБ</span>  

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

3.5 Объекты запроса и ответа

До сих пор мы использовали экземпляры java.lang.String качестве входных и выходных параметров. Тем не менее, мы не ограничены этим. Вместо этого мы можем использовать произвольные сложные структуры данных и позволить Amazon Lambda отобразить входные данные на экземпляры классов вашей модели и на экземпляры наших классов ответов.

В следующем примере мы будем использовать класс Order качестве входных данных для нашей функции Lambda и класс Response качестве возвращаемого значения:

01
02
03
04
05
06
07
08
09
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
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">package com.javacodegeeks.aws;</span> пакет com.javacodegeeks.aws;</span><font></font>
<font></font>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import org.apache.logging.log4j.LogManager;</span> import org.apache.logging.log4j.LogManager;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import org.apache.logging.log4j.Logger;</span> import org.apache.logging.log4j.Logger;</span><font></font>
<font></font>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import java.text.SimpleDateFormat;</span> import java.text.SimpleDateFormat;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import java.util.Date;</span> импорт java.util.Date;</span><font></font>
<font></font>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">public class RequestResponseHandler {</span> открытый класс RequestResponseHandler {</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">private static final Logger LOGGER = LogManager.getLogger(RequestResponseHandler.class);</span> private static final Logger LOGGER = LogManager.getLogger (RequestResponseHandler.class);</span><font></font>
<font></font>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">public static class Order {</span> открытый статический класс Order {</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">private String productId;</span> private String productId;</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">private String amount;</span> количество частных строк;</span><font></font>
<font></font>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">public String getProductId() {</span> public String getProductId () {</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">return productId;</span> вернуть productId;</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span><font></font>
<font></font>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">public void setProductId(String productId) {</span> public void setProductId (String productId) {</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">this.productId = productId;</span> this.productId = productId;</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span><font></font>
<font></font>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">public String getAmount() {</span> public String getAmount () {</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">return amount;</span> сумма возврата;</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span><font></font>
<font></font>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">public void setAmount(String amount) {</span> public void setAmount (String amount) {</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">this.amount = amount;</span> this.amount = количество;</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span><font></font>
<font></font>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">public static class Response {</span> открытый статический класс Response {</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">private String timestamp;</span> частная строка времени;</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">private String state;</span> частное строковое состояние;</span><font></font>
<font></font>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">public String getTimestamp() {</span> public String getTimestamp () {</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">return timestamp;</span> отметка времени возврата;</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span><font></font>
<font></font>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">public void setTimestamp(String timestamp) {</span> public void setTimestamp (String timestamp) {</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">this.timestamp = timestamp;</span> this.timestamp = timestamp;</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span><font></font>
<font></font>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">public String getState() {</span> public String getState () {</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">return state;</span> возвратное состояние;</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span><font></font>
<font></font>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">public void setState(String state) {</span> public void setState (String state) {</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">this.state = state;</span> this.state = состояние;</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span><font></font>
<font></font>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">public Response handleRequest(Order order) {</span> public Response handleRequest (Порядок заказа) {</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">LOGGER.info("Received order for product " + order.getProductId() + ": amount=" + order.getAmount());</span> LOGGER.info ("Получен заказ на товар" + order.getProductId () + ": amount =" + order.getAmount ());</span><font></font>
<font></font>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");</span> SimpleDateFormat sdf = new SimpleDateFormat ("гггг-ММ-дд'ТХЧ: мм: сс.СССЗ");</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">Response response = new Response();</span> Ответный ответ = новый Ответ ();</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">response.setTimestamp(sdf.format(new Date()));</span> response.setTimestamp (sdf.format (new Date ()));</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">response.setState("Shipped");</span> response.setState ( "погружены");</span><font></font>
<font></font>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">return response;</span> обратный ответ;</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>

Как видите, обработчик запроса принимает только один параметр типа Order . Если мы не используем его, мы можем опустить объект Context . Класс Order представляет собой простой POJO с методами получения и установки для полей productId и amount . Amazon Lambda автоматически сериализует любую данную строку JSON в эту структуру.

Следовательно, мы можем создать контрольный пример для этой функции, который использует следующий объект JSON в качестве входных данных:

1
2
3
4
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">{</span> {</span>
  <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"productId": "4711",</span> "productId": "4711",</span>
  <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"amount": 2</span> «количество»: 2</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>

Реализация обработчика просто выводит предоставленные значения. После этого он создает новый экземпляр Response и заполняет его текущей отметкой времени, а также состоянием. Выполнив этот простой обработчик, вы увидите следующий вывод:

1
2
3
4
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">{</span> {</span>
  <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"timestamp": "2018-06-16T13:07:20.441+0000",</span> «метка времени»: «2018-06-16T13: 07: 20.441 + 0000»,</span>
  <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"state": "Shipped"</span> "состояние": "отправлено"</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>

Вместе с log4j приведенный выше код полностью независим от Amazon AWS и поэтому может быть протестирован или использован повторно в различных средах исполнения.

3.6 Повторить и масштабировать

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

  • Ваша функция не завершается до истечения времени ожидания.
  • Входные данные не могут быть проанализированы.
  • Выполнение достигает предела ресурса (например, предела памяти).

Если вызов функции не инициируется источником на основе потока (например, потоки Amazon Kinesis или DynamoDB), мы должны различать синхронный и асинхронный вызов.

Синхронный вызов лямбда-функции, как, например, использование AWS SDK для прямого вызова функции, вызовет ошибку 429. Вызывающее приложение может решить, следует ли повторить попытку или нет.

Если лямбда-функция вызывается асинхронно, Amazon автоматически выполнит две попытки с задержкой между этими попытками. Если эти две попытки повторятся неудачно, и вы настроили «очередь недоставленных сообщений» (DLQ), запрос будет сохранен в DLQ и может быть обработан позже. В тех случаях, когда DLQ не настроен (по умолчанию), запрос отбрасывается и теряется.

Среда выполнения принимает другую форму обработки ошибок, если предполагается, что функция Lambda обрабатывает потоковые источники. В этом случае ошибка рассматривается как блокирующая единица, и дальнейшие события из потока не будут обрабатываться, пока не истечет срок действия событий. Это гарантирует, что все события обрабатываются в правильном порядке.

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

Источники событий на основе потока имеют один экземпляр для каждого сегмента потока. Если вы используете, например, 10 осколков DynamoDB, у вас будет 10 экземпляров вашей лямбда-функции, обрабатывающей события.

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

Для бесперебойной обработки пакетов Amazon Lambda при необходимости увеличит число одновременных экземпляров в больших фрагментах. Размер порции зависит от региона, в котором вы работаете. В регионе ЕС (Лондон) вы получите 500 новых экземпляров одновременно, а в ЕС (Франкфурт) вы получите 1000 и 3000 в ЕС (Ирландия). Amazon будет увеличивать количество одновременных экземпляров, пока их количество не станет достаточным для обработки всех запросов. Это также уменьшит количество экземпляров, если они больше не нужны.

4. Источники событий

В этой главе мы подробнее рассмотрим некоторые доступные источники событий, т.е. сервисы в космосе AWS, которые могут инициировать выполнение лямбда-функции.

4.1 S3

Популярный сервис в мире AWS — Amazon S3. Это хранилище объектов, которое можно использовать для хранения файлов любого типа. Каждый файл идентифицируется корзиной, в которой он хранится, а также его ключом внутри корзины. Контейнеры представляют собой набор файлов, т.е. вы можете использовать разные блоки для реализации различных типов шаблонов доступа пользователей или для разделения данных различных типов.

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

Следующий документ JSON представляет собой пример события, публикуемого Amazon S3 каждый раз, когда создается новый объект (взято отсюда ):

01
02
03
04
05
06
07
08
09
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
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">{</span> {</span
   <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"Records":[</span> "Запись": [</span
      <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">{</span> {</span
         <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"eventVersion":"2.0",</span> "EventVersion": "2,0",</span>
         <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"eventSource":"aws:s3",</span> "EventSource": "AWS: s3",</span>
         <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"awsRegion":"us-east-1",</span> "AwsRegion": "мы-восток-1",</span>
         <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"eventTime":"1970-01-01T00:00:00.000Z",</span> "EventTime": "1970-01-01T00: 00: 00.000Z",</span>
         <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"eventName":"ObjectCreated:Put",</span> "Именем_событие": "ObjectCreated: Помещенный",</span>
         <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"userIdentity":{</span> "UserIdentity": {</span
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"principalId":"AIDAJDPLRKLG7UEXAMPLE"</span> "PrincipalId": "AIDAJDPLRKLG7UEXAMPLE"</span>
         <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">},</span> },</span>
         <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"requestParameters":{</span> "requestParameters": {</span
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"sourceIPAddress":"127.0.0.1"</span> "SourceIPAddress": "127.0.0.1"</span>
         <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">},</span> },</span>
         <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"responseElements":{</span> "responseElements": {</span
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"x-amz-request-id":"C3D13FE58DE4C810",</span> "Х-АМЗ-запрос-идентификатор": "C3D13FE58DE4C810",</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"x-amz-id-2":"FMyUVURIY8/IgAtTv8xRjskZQpcIZ9KG4V5Wp6S7S/JRWeUWerMUE5JgHvANOjpD"</span> "Х-AMZ-ID-2": "FMyUVURIY8 / IgAtTv8xRjskZQpcIZ9KG4V5Wp6S7S / JRWeUWerMUE5JgHvANOjpD"</span>
         <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">},</span> },</span>
         <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"s3":{</span> "S3": {</span
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"s3SchemaVersion":"1.0",</span> "S3SchemaVersion": "1,0",</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"configurationId":"testConfigRule",</span> "ConfigurationId": "testConfigRule",</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"bucket":{</span> "Ведро": {</span
               <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"name":"mybucket",</span> "Название": "mybucket",</span>
               <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"ownerIdentity":{</span> "OwnerIdentity": {</span
                  <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"principalId":"A3NL1KOZZKExample"</span> "PrincipalId": "A3NL1KOZZKExample"</span>
               <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">},</span> },</span>
               <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"arn":"arn:aws:s3:::mybucket"</span> "ARN": "ARN: AWS: s3 ::: mybucket"</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">},</span> },</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"object":{</span> "Объект": {</span
               <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"key":"HappyFace.jpg",</span> "Ключ": "HappyFace.jpg",</span>
               <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"size":1024,</span> "Размер": 1024,</span>
               <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"eTag":"d41d8cd98f00b204e9800998ecf8427e",</span> "Etag": "d41d8cd98f00b204e9800998ecf8427e",</span>
               <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"versionId":"096fKKXTRTtl3on89fVO.nfljtsv6qko",</span> "VERSIONID": "096fKKXTRTtl3on89fVO.nfljtsv6qko",</span>
               <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"sequencer":"0055AED6DCD90281E5"</span> "Секвенсор": "0055AED6DCD90281E5"</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
         <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
      <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
   <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">]</span> ]</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>

Поскольку разбор JSON-документа уже выполнен инфраструктурой Lambda, все, что нам нужно сделать, — это предоставить полную структуру классов Java, на которую этот документ может быть отображен. К счастью, нам не нужно изобретать велосипед, поскольку Amazon публикует именно этот класс в следующем артефакте maven:

1
2
3
4
5
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><dependency></span> <Зависимость></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><groupId>com.amazonaws</groupId></span> <Идентификатор_группы> com.amazonaws </ идентификатор_группы></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><artifactId>aws-java-sdk-s3</artifactId></span> <Артефакт> AWS-ява-СДК-s3 </ артефакт></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><version>1.11.345</version></span> <Версия> 1.11.345 </ версия></span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"></dependency></span> </ Зависимость></span>

С помощью этой библиотеки мы можем реализовать нашу функцию Lambda следующим образом:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">package com.javacodegeeks.aws;</span> пакет com.javacodegeeks.aws;</span><font></font>
<font></font>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import com.amazonaws.services.lambda.runtime.Context;</span> import com.amazonaws.services.lambda.runtime.Context;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import com.amazonaws.services.lambda.runtime.RequestHandler;</span> import com.amazonaws.services.lambda.runtime.RequestHandler;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import com.amazonaws.services.lambda.runtime.events.S3Event;</span> import com.amazonaws.services.lambda.runtime.events.S3Event;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import com.amazonaws.services.s3.AmazonS3;</span> import com.amazonaws.services.s3.AmazonS3;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import com.amazonaws.services.s3.AmazonS3ClientBuilder;</span> import com.amazonaws.services.s3.AmazonS3ClientBuilder;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import com.amazonaws.services.s3.event.S3EventNotification;</span> import com.amazonaws.services.s3.event.S3EventNotification;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import com.amazonaws.services.s3.model.*;</span> import com.amazonaws.services.s3.model. *;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import org.apache.commons.io.IOUtils;</span> import org.apache.commons.io.IOUtils;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import org.apache.logging.log4j.LogManager;</span> import org.apache.logging.log4j.LogManager;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import org.apache.logging.log4j.Logger;</span> import org.apache.logging.log4j.Logger;</span><font></font>
<font></font>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import javax.imageio.ImageIO;</span> импорт javax.imageio.ImageIO;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import java.awt.*;</span> импорт java.awt. *;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import java.awt.image.BufferedImage;</span> import java.awt.image.BufferedImage;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import java.io.ByteArrayInputStream;</span> import java.io.ByteArrayInputStream;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import java.io.ByteArrayOutputStream;</span> import java.io.ByteArrayOutputStream;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import java.io.IOException;</span> импорт java.io.IOException;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import java.util.List;</span> import java.util.List;</span><font></font>
<font></font>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">public class S3EventHandler implements RequestHandler<S3Event, String> {</span> открытый класс S3EventHandler реализует RequestHandler <S3Event, String> {</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">private static final Logger LOGGER = LogManager.getLogger(S3EventHandler.class);</span> приватная статическая финальная Logger LOGGER = LogManager.getLogger (S3EventHandler.class);</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">static final int THUMBNAIL_WIDTH = 512;</span> статический финал int THUMBNAIL_WIDTH = 512;</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">static final int THUMBNAIL_HEIGHT = 512;</span> static final int THUMBNAIL_HEIGHT = 512;</span><font></font>
<font></font>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">@Override</span> @Override</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">public String handleRequest(S3Event s3Event, Context context) {</span> public String handleRequest (S3Event s3Event, Контекстный контекст) {</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">LOGGER.info("Invoked " + S3EventHandler.class.getSimpleName() + " with "</span> LOGGER.info («Вызван» + S3EventHandler.class.getSimpleName () + «с»</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">+ (s3Event.getRecords() == null ? 0 : s3Event.getRecords().size()) + " records.");</span> + (s3Event.getRecords () == null? 0: s3Event.getRecords (). size ()) + "records.");</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">List records = s3Event.getRecords();</span> Список записей = s3Event.getRecords ();</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">if (records != null) {</span> if (records! = null) {</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">for (S3EventNotification.S3EventNotificationRecord record : records) {</span> для записи (S3EventNotification.S3EventNotificationRecord: records) {</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">String eventName = record.getEventName();</span> String eventName = record.getEventName ();</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">if ("ObjectCreated:Put".equals(eventName)) {</span> if ("ObjectCreated: Put" .equals (eventName)) {</span>
                    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">S3EventNotification.S3Entity s3 = record.getS3();</span> S3EventNotification.S3Entity s3 = record.getS3 ();</span>
                    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">if (s3 != null) {</span> if (s3! = null) {</span>
                        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">String bucketName = s3.getBucket().getName();</span> String bucketName = s3.getBucket (). GetName ();</span>
                        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">String key = s3.getObject().getKey();</span> String key = s3.getObject (). GetKey ();</span>
                        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">if (key.endsWith(".jpg") || key.endsWith(".jpeg")) {</span> if (key.endsWith (". jpg") || key.endsWith (". jpeg")) {</span>
                            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">AmazonS3 client = AmazonS3ClientBuilder.defaultClient();</span> Клиент AmazonS3 = AmazonS3ClientBuilder.defaultClient ();</span>
                            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, key);</span> GetObjectRequest getObjectRequest = new GetObjectRequest (bucketName, key);</span>
                            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">S3Object s3Object = client.getObject(getObjectRequest);</span> S3Object s3Object = client.getObject (getObjectRequest);</span>
                            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">S3ObjectInputStream inputStream = s3Object.getObjectContent();</span> S3ObjectInputStream inputStream = s3Object.getObjectContent ();</span>
                            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">try {</span> пытаться {</span>
                                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">byte[] bytes = IOUtils.toByteArray(inputStream);</span> byte [] bytes = IOUtils.toByteArray (inputStream);</span>
                                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">byte[] scaledBytes = scaleImage(bytes);</span> byte [] scaledBytes = scaleImage (bytes);</span>
                                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">uploadThumbnailImage(bucketName, key, client, scaledBytes);</span> uploadThumbnailImage (bucketName, ключ, клиент, scaledBytes);</span>
                                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">LOGGER.info("Successfully created thumbnail image.");</span> LOGGER.info («Успешно созданное эскизное изображение.»);</span>
                            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">} catch (IOException e) {</span> } catch (IOException e) {</span>
                                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">LOGGER.error("Failed to get content of S3 object (bucket=" + bucketName</span> LOGGER.error ("Не удалось получить содержимое объекта S3 (bucket =" + bucketName</span>
                                        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">+ ", key=" + key + "): " + e.getMessage(), e);</span> + ", key =" + key + "):" + e.getMessage (), e);</span>
                            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
                        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">} else {</span> } еще {</span>
                            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">LOGGER.debug("Key does not end with .jpg or .jpeg.");</span> LOGGER.debug («Ключ не заканчивается на .jpg или .jpeg.»);</span>
                        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
                    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">} else {</span> } еще {</span>
                        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">LOGGER.debug("No S3 object in Record.");</span> LOGGER.debug («Нет объекта S3 в записи.»);</span>
                    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">} else {</span> } еще {</span>
                    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">LOGGER.debug("Ignoring record (not a put request).");</span> LOGGER.debug («Игнорирование записи (не пут-запрос).»);</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">return "OK";</span> вернуть «ОК»;</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span><font></font>
<font></font>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">private void uploadThumbnailImage(String bucketName, String key, AmazonS3 client, byte[] scaledBytes) {</span> private void uploadThumbnailImage (строковое bucketName, строковый ключ, клиент AmazonS3, byte [] scaledBytes) {</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">int lastIndexOfDot = key.lastIndexOf('.');</span> int lastIndexOfDot = key.lastIndexOf ('.');</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">String newKey = key.substring(0, lastIndexOfDot) + "_thumb"</span> String newKey = key.substring (0, lastIndexOfDot) + "_thumb"</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">+ key.substring(lastIndexOfDot+1);</span> + key.substring (lastIndexOfDot + 1);</span><font></font>
<font></font>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">ObjectMetadata metadata = new ObjectMetadata();</span> ObjectMetadata metadata = new ObjectMetadata ();</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">metadata.setContentLength(scaledBytes.length);</span> metadata.setContentLength (scaledBytes.length);</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, newKey,</span> PutObjectRequest putObjectRequest = new PutObjectRequest (bucketName, newKey,</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">new ByteArrayInputStream(scaledBytes), metadata);</span> новый ByteArrayInputStream (scaledBytes), метаданные);</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">client.putObject(putObjectRequest);</span> client.putObject (putObjectRequest);</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span><font></font>
<font></font>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">private byte[] scaleImage(byte[] bytes) throws IOException {</span> закрытый байт [] scaleImage (byte [] bytes) выдает IOException {</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">BufferedImage img = ImageIO.read(new ByteArrayInputStream(bytes));</span> BufferedImage img = ImageIO.read (new ByteArrayInputStream (bytes));</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">int width = img.getWidth();</span> int width = img.getWidth ();</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">int height = img.getHeight();</span> int height = img.getHeight ();</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">if (width > 0 && height > 0) {</span> if (width> 0 && height> 0) {</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">int newWidth = THUMBNAIL_WIDTH;</span> int newWidth = THUMBNAIL_WIDTH;</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">int newHeight = THUMBNAIL_HEIGHT;</span> int newHeight = THUMBNAIL_HEIGHT;</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">if (width > THUMBNAIL_WIDTH || height > THUMBNAIL_HEIGHT) {</span> if (ширина> THUMBNAIL_WIDTH || высота> THUMBNAIL_HEIGHT) {</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">if (width >= height) {</span> if (ширина> = высота) {</span>
                    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">newWidth = THUMBNAIL_WIDTH;</span> newWidth = THUMBNAIL_WIDTH;</span>
                    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">newHeight = (int)((THUMBNAIL_WIDTH / (double)width) * (double)height);</span> newHeight = (int) ((THUMBNAIL_WIDTH / (double) width) * (double) height);</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">} else {</span> } еще {</span>
                    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">newHeight = THUMBNAIL_HEIGHT;</span> newHeight = THUMBNAIL_HEIGHT;</span>
                    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">newWidth = (int)((THUMBNAIL_WIDTH / (double)height) * (double)width);</span> newWidth = (int) ((THUMBNAIL_WIDTH / (double) height) * (double) width);</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">Image scaledInstance = img.getScaledInstance(newWidth, newHeight, Image.SCALE_DEFAULT);</span> Изображение scaledInstance = img.getScaledInstance (newWidth, newHeight, Image.SCALE_DEFAULT);</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">BufferedImage thumbnail = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_3BYTE_BGR);</span> BufferedImage thumbnail = new BufferedImage (newWidth, newHeight, BufferedImage.TYPE_3BYTE_BGR);</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">Graphics2D graphics = thumbnail.createGraphics();</span> Graphics2D graphics = thumbnail.createGraphics ();</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">graphics.drawImage(scaledInstance, 0, 0, null);</span> graphics.drawImage (scaledInstance, 0, 0, null);</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">ByteArrayOutputStream outputStream = new ByteArrayOutputStream();</span> ByteArrayOutputStream outputStream = new ByteArrayOutputStream ();</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">ImageIO.write(thumbnail, "jpg", outputStream);</span> ImageIO.write (thumbnail, "jpg", outputStream);</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">return outputStream.toByteArray();</span> return outputStream.toByteArray ();</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">} else {</span> } еще {</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">return bytes;</span> возврат байтов;</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>

Функция-обработчик перебирает доступные записи и отфильтровывает те, которые соответствуют имени события «ObjectCreated: Put» и чья клавиша S3 заканчивается либо «.jpeg», либо «.jpg». В этом случае он создает новый клиентский объект Amazon S3 для загрузки данных изображения непосредственно из S3 с использованием GetObjectRequest . Содержимое объекта S3 предоставляется как S3ObjectInputStream , который можно преобразовать в байтовый массив. Массив байтов можно масштабировать с использованием стандартного API Java. Обратите внимание, что Amazon, похоже, использует openjdk в своей инфраструктуре Lambda, поэтому могут присутствовать не все кодировщики. В нашем примере мы должны использовать, например, изображение типа TYPE_3BYTE_BGR для записи изображения jpg.

Наконец, что еще важнее, мы создаем новый ключ, содержащий суффикс _thumb и загружаем новое изображение в сервис Amazon S3.

Для выполнения этой функции роль, под которой она выполняется, должна иметь доступ на чтение и запись к S3. Требуемые привилегии могут быть предоставлены роли в IAM путем назначения этой AmazonS3FullAccess политики AmazonS3FullAccess :

Amazon Lambda s3Policy

Рис. 11: s3Policy

Загрузив функцию Lambda, как описано выше, мы можем перейти к нашей корзине S3 и зарегистрировать новую функцию Lambda в разделе «Свойства» в качестве триггера для новых объектов:

Amazon Lambda s3Events

Рис. 12: s3Events

Рядом с именем и типом запросов должна вызываться функция, мы можем выбрать дополнительный префикс и суффикс и, конечно же, нашу функцию Lambda.

Когда мы впоследствии загружаем новое изображение с шириной или высотой более 512 пикселей, новая лямбда-функция должна быть выполнена и создает новое миниатюрное изображение в том же сегменте. Вы можете следить за его выполнением в CloudWatch.

4.2 DynamoDB

DynamoDB — это база данных NoSQL, которая позволяет хранить произвольные элементы данных в таблицах. Элементы похожи на строки в реляционных базах данных, но в отличие от реляционных баз данных DynamoDB не имеет схемы, т. Е. Кроме ключа раздела и необязательного ключа сортировки каждый элемент может иметь различные атрибуты. Ключ разделения используется для распределения данных по разным сегментам, в то время как ключ сортировки определяет порядок хранения данных с одним и тем же ключом разделения. Наличие ключа сортировки позволяет перебирать данные в отсортированном виде, что удобно, например, когда вы хотите отобразить, например, все события доступа определенного пользователя, отсортированные по времени. В этом случае вы должны использовать идентификатор пользователя в качестве ключа раздела для таблицы событий и метку времени события в качестве ключа сортировки.

Триггер должен обрабатывать все изменения в таблице, к которой он прикреплен. Следовательно, потоки DynamoDB являются источником событий на основе потоков для лямбда-функций. Это означает, что функция вызывается для каждого события до тех пор, пока она либо не обработает событие успешно, либо время не истечет. Блокировка до успешной обработки события гарантирует, что триггер обрабатывает события в правильном порядке и что каждое событие может быть обработано правильно.

Пример события из DynamoDB выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">{</span> {</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"Records": [{</span> «Записи»: [{</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"eventID": "1",</span> "eventID": "1",</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"eventVersion": "1.0",</span> "eventVersion": "1.0",</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"dynamodb": {</span> "Dynamodb": {</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"Keys": {</span> "Ключи": {</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"Id": {</span> "Мне бы": {</span>
                    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"N": "101"</span> "N": "101"</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">},</span> },</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"NewImage": {</span> "Новое изображение": {</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"Message": {</span> «Сообщение»: {</span>
                    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"S": "New item!"</span> "S": "Новый предмет!"</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">},</span> },</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"Id": {</span> "Мне бы": {</span>
                    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"N": "101"</span> "N": "101"</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">},</span> },</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"StreamViewType": "NEW_AND_OLD_IMAGES",</span> "StreamViewType": "NEW_AND_OLD_IMAGES",</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"SequenceNumber": "111",</span> "SequenceNumber": "111",</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"SizeBytes": 26</span> "SizeBytes": 26</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">},</span> },</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"awsRegion": "us-west-2",</span> "awsRegion": "us-west-2",</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"eventName": "INSERT",</span> "eventName": "INSERT",</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"eventSourceARN": eventsourcearn,</span> "eventSourceARN": eventsourcearn,</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"eventSource": "aws:dynamodb"</span> "eventSource": "aws: Dynamodb"</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}]</span> }]</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>

Подобно событиям S3, у нас есть массив записей с eventSource , awsRegion , awsRegion и eventVersion . eventName сообщает нам тип события (здесь: INSERT ). Атрибут dynamodb предоставляет более подробную информацию: Keys содержат Keys разделения и сортировки, NewImage новые значения и OldImage старые значения (если доступны).

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

Поскольку библиотека aws-java-sdk-dynamodb уже предоставляет готовые структуры данных для событий DynamoDB, мы добавляем зависимость от нее в наш pom.xml :

1
2
3
4
5
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><dependency></span> <Зависимость></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><groupId>com.amazonaws</groupId></span> <Идентификатор_группы> com.amazonaws </ идентификатор_группы></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><artifactId>aws-java-sdk-dynamodb</artifactId></span> <Артефакт> AWS-ява-СДК-dynamodb </ артефакт></span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"><version>1.11.346</version></span> <Версия> 1.11.346 </ версия></span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left"></dependency></span> </ Зависимость></span>

Теперь мы можем реализовать простой RequestHandler который обрабатывает событие DynamoDB и регистрирует все его интересные части:

01
02
03
04
05
06
07
08
09
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
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">package com.javacodegeeks.aws;</span> пакет com.javacodegeeks.aws;</span><font></font>
<font></font>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import com.amazonaws.services.dynamodbv2.model.AttributeValue;</span> import com.amazonaws.services.dynamodbv2.model.AttributeValue;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import com.amazonaws.services.dynamodbv2.model.StreamRecord;</span> import com.amazonaws.services.dynamodbv2.model.StreamRecord;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import com.amazonaws.services.lambda.runtime.Context;</span> import com.amazonaws.services.lambda.runtime.Context;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import com.amazonaws.services.lambda.runtime.RequestHandler;</span> import com.amazonaws.services.lambda.runtime.RequestHandler;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import com.amazonaws.services.lambda.runtime.events.DynamodbEvent;</span> import com.amazonaws.services.lambda.runtime.events.DynamodbEvent;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import org.apache.logging.log4j.LogManager;</span> import org.apache.logging.log4j.LogManager;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import org.apache.logging.log4j.Logger;</span> import org.apache.logging.log4j.Logger;</span><font></font>
<font></font>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import java.util.List;</span> import java.util.List;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import java.util.Map;</span> импорт java.util.Map;</span><font></font>
<font></font>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">public class DynamoDbEventHandler implements RequestHandler<DynamodbEvent, String> {</span> открытый класс DynamoDbEventHandler реализует RequestHandler <DynamodbEvent, String> {</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">private static final Logger LOGGER = LogManager.getLogger(DynamoDbEventHandler.class);</span> приватная статическая финальная Logger LOGGER = LogManager.getLogger (DynamoDbEventHandler.class);</span><font></font>
<font></font>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">@Override</span> @Override</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">public String handleRequest(DynamodbEvent dynamodbEvent, Context context) {</span> public String handleRequest (DynamodbEvent dynamicodbEvent, Контекстный контекст) {</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">List records = dynamodbEvent.getRecords();</span> Список записей = dynamicodbEvent.getRecords ();</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">if (records != null) {</span> if (records! = null) {</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">for (DynamodbEvent.DynamodbStreamRecord record : records) {</span> для (запись DynamodbEvent.DynamodbStreamRecord: записи) {</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">StringBuilder sb = new StringBuilder();</span> StringBuilder sb = new StringBuilder ();</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">sb.append("eventName=").append(record.getEventName());</span> sb.append ( "EventName =") Append (record.getEventName ()).</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">StreamRecord dynamodb = record.getDynamodb();</span> StreamRecord dynamodb = record.getDynamodb ();</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">if (dynamodb != null) {</span> if (динамодб! = ноль) {</span>
                    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">sb.append(";keys=");</span> sb.append ( "; клавиши =");</span>
                    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">appendMap(sb, dynamodb.getKeys());</span> appendMap (sb, dynamicodb.getKeys ());</span>
                    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">sb.append(";oldImage=");</span> sb.append ( "; oldImage =");</span>
                    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">appendMap(sb, dynamodb.getOldImage());</span> appendMap (sb, dynamicodb.getOldImage ());</span>
                    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">sb.append(";newImage=");</span> sb.append ( "; newImage =");</span>
                    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">appendMap(sb, dynamodb.getNewImage());</span> appendMap (sb, dynamicodb.getNewImage ());</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">LOGGER.info("Record: " + sb.toString());</span> LOGGER.info ("Запись:" + sb.toString ());</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">return "OK";</span> вернуть «ОК»;</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span><font></font>
<font></font>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">private void appendMap(StringBuilder sb, Map<String, AttributeValue> map) {</span> private void appendMap (StringBuilder sb, Map <String, AttributeValue> map) {</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">if (map != null) {</span> if (map! = null) {</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">int count = 0;</span> int count = 0;</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">for (Map.Entry<String, AttributeValue> entry : map.entrySet()) {</span> для (Map.Entry <String, AttributeValue> entry: map.entrySet ()) {</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">if (count > 0) {</span> if (count> 0) {</span>
                    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">sb.append(",");</span> sb.append ( "");</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">sb.append(entry.getKey()).append("/").append(entry.getValue());</span> .. Sb.append (entry.getKey ()) Append ( "/") Append (entry.getValue ());</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">count++;</span> подсчитывать ++;</span>
            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>

Как и в случае событий S3, мы перебираем список с помощью DynamodbStreamRecord и извлекаем его ключи, старые и новые значения.

Однако прежде чем мы сможем использовать этот новый RequestHandler нам нужно настроить таблицу DynamoDB и настроить триггер. Поэтому в консоли AWS мы переходим к сервису «DynamoDB» и нажимаем «Создать таблицу»:

Amazon Lambda DynamoDbCreateTable

Рис. 13: DynamoDbCreateTable

Следующий диалог запрашивает у нас некоторую основную информацию о новой таблице. Мы должны предоставить имя и первичный ключ. В нашем примере мы создаем ключ раздела с ID имени типа String и не используем ключ сортировки:

Amazon Lambda DynamoDbCreateTable2

Рис. 14: DynamoDbCreateTable2

Настройки по умолчанию для индексов и предоставленная емкость для чтения и записи в порядке для нашего варианта использования здесь. Нажмите «Создать», чтобы AWS создал для нас новую таблицу.

Прежде чем мы сможем добавить триггер, мы должны прикрепить привилегию для чтения лямбда-потока к роли, которая будет выполнять функцию Lambda. Это можно сделать, перейдя к службе IAM, выбрав роль и добавив политику, AWSLambdaInvocation-DynamoDBкак показано на следующем снимке экрана:

Amazon Лямбда динамоDbCreateTriggerrole

Рис. 15: DynamoDbCreateTriggerrole

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

Amazon Лямбда динамоDbCreateTrigger

Рис. 16: DynamoDbCreateTrigger

Следующий диалог просит нас выбрать функцию Lambda и размер пакета:

Amazon Lambda DynamoDbCreateTrigger2

Рис. 17: DynamoDbCreateTrigger2

Если щелкнуть «Создать», наш код будет выполняться каждый раз, когда элемент обновляется в таблице, к которой мы прикрепили триггер.

Чтобы проверить это, мы выбираем вкладку «Предметы» на странице сведений о таблице и нажимаем «Создать предмет»:

Amazon Lambda DynamoDbCreateItem

Рис. 18: DynamoDbCreateItem

С помощью следующего диалога мы можем отправить документ JSON, который содержит хотя бы значение для атрибута ID:

Amazon Lambda DynamoDbCreateItem2

Рис. 19: DynamoDbCreateItem2

Кроме того, мы также добавляем значение для атрибута Name. Мы можем использовать этот атрибут, так как он не является частью первичного ключа, и DynamoDB, поскольку база данных без схемы не будет проверять наши дополнительные атрибуты по любому виду схемы.

Нажатие «Сохранить» не должно запускать нашу функцию Lambda. Вы можете убедиться в этом, перейдя в сервис «CloudWatch» и проверив журналы для нашей функции Lambda:

1
2018-06-16 13:43:35 6453ae2b-af62-4090-93d0-b5607e05ee55 INFO DynamoDbEventHandler:33 - Record: eventName=INSERT;keys=ID/{S: 4711,};oldImage=;newImage=ID/{S: 4711,},Name/{S: Java Code Geek,}

Как и ожидалось, оператор журнала показывает название события, ключ и новые значения. В случае события обновления вы также увидите старое значение.

4.3 Шлюз API

Еще одна интересная услуга в мире AWS в сочетании с Lambda — это услуга «API Gateway». Его можно использовать для предоставления общедоступного REST-API, поддерживаемого функциями Lambda.

Для простоты мы реализуем функцию Lambda, которая возвращает текущую метку времени и может быть вызвана HTTP-запросом GET.

Код для этой функции выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
package com.javacodegeeks.aws;<font></font>
<font></font>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import java.text.SimpleDateFormat;</span> import java.text.SimpleDateFormat;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import java.util.Date;</span> импорт java.util.Date;</span><font></font>
<font></font>
public class APIGatewayHandler {<font></font>
<font></font>
    public String handleRequest() {<font></font>
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");<font></font>
        return sdf.format(new Date());<font></font>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>

Как обычно, мы загружаем соответствующий jar-файл и регистрируем метод как лямбда-функцию с именем «apiGatewayFunction».

Создав функцию Lambda, мы переходим на стартовую страницу сервиса «API Gateway» и нажимаем «Создать API»:

Amazon лямбда createAPI

Рис. 20: создание API

Здесь мы выбираем «Новый API», предоставляем имя и описание для него и тип конечной точки. Конечная точка в терминологии AWS — это место, где подается контент. По умолчанию используется значение «Региональный», что означает, что API расположен только в регионе этого API и не распространяется на другие конечные точки.

Как только API был создан, мы можем создать новый «ресурс». «Ресурс» — это с технической точки зрения часть URL-адреса после имени сервера и порта. Новый ресурс можно создать через меню «Действия»:

Amazon Lambda createAPICreateResource

Рис. 21: createAPICreateResource

В следующем диалоговом окне запрашивается имя ресурса и путь:

Amazon Lambda createAPICreateResource2

Рис. 22: createAPICreateResource2

Кроме того, вы можете включить Cross-Origin-Resource-Sharing (CORS) и настроить этот ресурс как прокси-ресурс, что означает, что запросы перенаправляются без взаимодействия с API Gateway на веб-сервер в серверной части. Поскольку нам не нужны оба варианта, мы оставляем их отключенными.

После этого мы можем добавить новый метод HTTP для нового ресурса:

Amazon Lambda createAPICreateResource3

Рис. 23: createAPICreateResource3

В этом простом примере мы выбрали запрос GET и установили «лямбда-функцию» в качестве типа интеграции. Затем мы выбираем регион, в котором была определена лямбда-функция, и выбираем имя функции. Мы используем время ожидания по умолчанию и не включаем интеграцию прокси. Нажатие «Сохранить» приводит нас к следующей диаграмме:

Amazon Lambda createAPICreateResource4

Рис. 24: reateAPICreateResource4

Он визуализирует поток данных для только что настроенного метода HTTP. Клиент отправляет свои запросы, которые преобразуются в запрос интеграции типа «лямбда». Этот запрос на интеграцию вызывает нашу функцию Lambda. Его ответ в форме интеграционного ответа преобразуется в ответ метода и, наконец, возвращается клиенту.

Теперь, когда мы определили ресурс и один метод GET для этого ресурса, мы можем развернуть новый API. Это делается через меню «Действия» и вызывает следующий диалог:

Amazon Lambda createAPICreateResource5

Рис. 25: reateAPICreateResource5

Мы можем определить различные этапы развертывания, чтобы разделить разные версии нашего API. Таким образом, мы можем иметь одно «производственное» развертывание, одно «подготовительное» развертывание и так далее. В этом уроке мы создаем новое развертывание с именем «Test» и описанием «Test stage». В качестве описания развертывания мы выбрали «v0.1».

Нажатие «Deploy» освобождает наш API и показывает его URL:

Amazon Lambda createAPICreateResource6

Рис. 26: createAPICreateResource6

Вы можете видеть, что имя этапа является самой первой частью пути URL. Чтобы вызвать нашу функцию Lambda, мы добавляем имя ресурса, который мы определили выше (здесь lambda:), и вызываем URL в нашем веб-браузере:

Amazon Lambda createAPICreateResource7

Рис. 27: createAPICreateResource7

Как и ожидалось, функция Lambda возвращает текущую метку времени.

5. Версии и псевдонимы

В этом уроке мы создали разные лямбда-функции. Чтобы получить общее представление о том, в какой версии развернуто, AWS предлагает нам возможность определить версии и псевдонимы.

Создание версии означает создание снимка текущего кода и сохранение его вместе с описанием. Это можно сделать, выбрав Publish new versionиз Actionsменю. Появится следующее диалоговое окно:

Amazon Lambda создать новую версию

Рис. 28: создание новой версии

Вас попросят дать описание, а затем вы можете нажать Publish. AWS автоматически назначит версию 1 в качестве метки.

Кроме того, версия также имеет ARN, который содержит имя функции Lambda, а также ее версию. Это может выглядеть примерно так:

1
arn:aws:lambda:eu-central-1:054390200838:function:s3EventHandler:1

ARN означает «Название ресурса Amazon» и однозначно определяет ресурс в мире AWS. С помощью этого ARN теперь вы можете ссылаться на версию 1 нашей лямбда-функции «s3EventHandler» везде, где нам это нужно. В диалоговом окне конфигурации S3, где была выбрана лямбда-функция, которая будет выполняться для каждого нового объекта, теперь мы можем поместить этот ARN и сказать S3 использовать именно эту версию.

Тем не менее, что, если на конкретную функцию Lambda ссылаются в разных сервисах? Нужно ли переходить на все страницы конфигурации и обновлять ссылку после публикации новой версии нашей функции? Ответ, конечно: нет. AWS позволяет нам определять псевдонимы, которые указывают на конкретную версию лямбда-функции. Вместо прямой ссылки на конкретную версию мы можем создать псевдоним и указать его для этой версии функции.

Это делается через Actionsменю, используя пункт меню Create alias:

Амазонка лямбда

Рис. 29: createNewAlias

Этот диалог запрашивает у нас имя для псевдонима. Здесь мы просто выбрали «ПРОИЗВОДСТВО», чтобы указать, что это готовая к работе версия нашей функции. Помимо описания, мы также должны предоставить версию лямбда-функции, на которую указывает наш псевдоним.

Как вы можете видеть на скриншоте, можно определить дополнительную версию и, после выбора дополнительной версии, также вес. Это позволяет AWS распространять только определенное количество запросов к этому псевдониму для одной версии функции, а оставшееся количество — для другой версии. Этот шаблон известен как «канарейка выпуска», то есть вы можете протестировать новую версию с небольшим количеством пользователей. Если что-то пойдет не так, это повлияет на небольшое количество пользователей, и вы можете отключить новую версию. Если с новой версией вашего кода все в порядке, вы можете постепенно увеличивать количество пользователей.

Обратите внимание, что вы можете обновить псевдоним в отличие от версии после ее создания. Таким образом, вы можете, конечно, указать свой псевдоним «PRODUCTION» на новую версию своего кода. Однако версия 1 останется версией 1. Вы не можете изменить версию после ее публикации.

6. Лучшие практики

В этом разделе мы обсудим некоторые лучшие практики при работе с лямбда-функциями.

Для лучшего модульного тестирования нашего кода имеет смысл реализовать логику функции в отдельном методе или даже классе, не передавая Contextобъект AWS . Мы можем извлечь нужные значения из контекста и передать их другому методу:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
package com.javacodegeeks.aws;<font></font>
<font></font>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import com.amazonaws.services.lambda.runtime.Context;</span> import com.amazonaws.services.lambda.runtime.Context;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import com.amazonaws.services.lambda.runtime.RequestHandler;</span> import com.amazonaws.services.lambda.runtime.RequestHandler;</span><font></font>
<font></font>
public class SeparateHandler implements RequestHandler<String, String> {<font></font>
<font></font>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">@Override</span> @Override</span><font></font>
    public String handleRequest(String input, Context context) {<font></font>
        return echoString(input, context.getAwsRequestId());<font></font>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span><font></font>
<font></font>
    String echoString(String input, String awsRequestId) {<font></font>
        return input + " (AWS-Request-ID: " + awsRequestId + ")";<font></font>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>

В приведенном выше примере реализована «логика» внутри echoStringметода, которая может быть протестирована без необходимости создания Contextобъекта. Если бы мы даже извлекли код в отдельный класс, можно было бы использовать этот код внутри другого приложения без каких-либо лямбда-зависимостей.

AWS будет хранить в памяти определенное количество экземпляров вашей лямбда-функции, чтобы иметь возможность немедленно выполнять запросы. Поэтому вы можете попытаться инициализировать статические переменные в самый первый раз, когда используется значение, а затем повторно использовать результат вычисления из статической переменной. Таким образом, вам не нужно каждый раз создавать одни и те же объекты.

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

Среда выполнения предоставляет виртуальную машину для выполнения кода Java. Кроме того, он предоставляет среду выполнения Java (JRE). Поскольку вы не можете указать точную версию JVM для использования, вы должны попытаться самостоятельно контролировать зависимости, так как со временем код JDK, предоставляемый средой выполнения, может измениться и внести незначительные изменения. Следовательно, может иметь смысл полагаться на внешние зависимости, которые включены в наш загруженный jar, вместо того, чтобы полагаться на код при условии среды выполнения.

Поскольку вы должны платить за вызовы ваших функций Lambda, вы должны быть осторожны с рекурсией. Убедитесь, что критерий завершения выполняется каждый раз, потому что в противном случае бесконечные вызовы одной и той же функции могут привести к дорогостоящим затратам.

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

1
REPORT RequestId: 5cfc1f27-6e3c-11e8-bbb9-c79d86ee7e9f Duration: 110.00 ms Billed Duration: 200 ms Memory Size: 512 MB Max Memory Used: 73 MB 

Этот пример показывает, что мы установили 512 МБ памяти для этой функции, но она использовала только 73 МБ. Поэтому мы должны настроить параметры памяти и определить в стресс-тесте настройку памяти, которая не слишком мала, но позволяет нашей функции работать хорошо.

7. Ценообразование

Вы платите за время и память, которые потребляют все ваши функции Lambda. В рамках бесплатного уровня у вас есть 1 млн запросов в месяц и 400 000 ГБ-секунд в месяц бесплатно. Когда вы достигнете лимитов бесплатного уровня, которые не ограничиваются первыми двенадцатью месяцами новой учетной записи AWS, вы должны будете платить 0,0000002 доллара США за запрос и 0,00001667 доллара США за каждую секунду в ГБ.

Чтобы лучше это понять, давайте создадим простой пример. Предположим, у вас есть одна лямбда-функция, которая требует 512 МБ памяти и выполняется в среднем за одну секунду. Эта функция вызывается около пяти миллионов раз в месяц.

Вы можете сначала вычислить ГБ-секунды: 5 000 000 * 1 с * 512 МБ / 1024 = 2 500 000 ГБ-секунд. Поскольку первые 400 000 ГБ-секунд бесплатны; Вы должны заплатить за 2 100 000 ГБ-секунд. С ценой $ 0,00001667 за каждую секунду в ГБ вы платите $ 35,007 за вычисления.

Тогда у вас есть 5 млн запросов. С 1 млн запросов бесплатно, вам придется оплатить 4 млн запросов. Один запрос стоит $ 0,0000002, то есть 4 млн запросов стоят $ 0,8.

В общей сложности вам придется заплатить 35,807 долларов.

8. Загрузите исходный код

Это был Amazon Lambda Tutorial.