Как повысить производительность с помощью бессерверных архитектур

1656649237 kak povysit proizvoditelnost s pomoshhyu besservernyh arhitektur

Доменико Ангилетта

1*HFLyV4TeqKVMBgOALYB2yw
Фото Джесси Дарленда на Unsplash

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

Проблема

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

В этой статье я опишу бессерверную архитектуру на основе AWS, чрезвычайно масштабируемую и экономически эффективную.

Но начнём с самого начала. В одном из моих последних проектов, веб-приложений на рынке, где пользователи должны загрузить изображение продукта, который они хотят продать, оригинальное изображение сначала обрезается до правильного соотношения (4:3). Затем он превращается в три разных формата, которые используются в разных местах интерфейсной программы: 800x600px, 400x300px и 200x150px.

Являясь разработчиком Ruby on Rails, моим первым подходом было использование RubyGem – в частности Paperclip или Dragonfly, которые оба используют ImageMagick для обработки изображений.

Хотя эта реализация достаточно проста (поскольку это в основном просто конфигурация), существуют различные недостатки, которые могут возникнуть:

  1. Изображения обрабатываются на сервере приложений. Это может увеличить общее время ответа из-за большой нагрузки на ЦБ
  2. Сервер приложений имеет ограниченную вычислительную мощность, которая заранее настроена, и не подходит для обработки пакетных запросов. Если многие изображения необходимо обработать одновременно, мощность сервера может быть исчерпана в течение длительного периода времени. Увеличение вычислительной мощности с другой стороны приведет к более высоким затратам.
  3. Изображения обрабатываются последовательно. Опять же, если многие изображения нужно обработать одновременно, скорость может быть очень низкой.
  4. Если настроить неправильно, эти драгоценные элементы сохраняют обработанные изображения на диске, из-за чего на сервере может быстро иссякнуть место.

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

Решение

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

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

Оказывается, AWS Lambda идеально подходит для такого рода проблем. С одной стороны, он может обрабатывать тысячи запросов в секунду, а с другой стороны вы платите только за потребленное время вычисления. Если код не работает, плата не взимается.

AWS S3 предоставляет огромную память по очень низкой цене, тогда как AWS SNS обеспечивает простой способ обмена сообщениями Pub/Sub для микросервисов, распределенных систем и бессерверных программ. Наконец, AWS Cloudfront используется как сеть доставки содержимого для изображений, хранящихся на S3.

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

Архитектура высокого уровня

Процесс создания различных версий изображения с исходного изображения начинается с загрузки оригинального изображения на AWS S3. Это запускает через AWS SNS выполнение функции AWS Lambda, отвечающей за создание новых версий изображений и их повторную загрузку на AWS S3. Вот последовательность более подробно:

  1. Изображения загружаются в определенную папку внутри сегмента AWS S3
  2. Каждый раз, когда новое изображение загружается в эту папку, S3 публикует сообщение, содержащее ключ S3 созданного объекта в теме AWS SNS
  3. AWS Lambda, настроенный как потребитель по той же теме SNS, читает новое сообщение и использует ключ объекта S3 для получения нового изображения
  4. AWS Lambda обрабатывает новое изображение, применяя необходимые преобразования, и загружает обработанные изображения в S3
  5. Теперь обработанные изображения предоставляются конечным пользователям через AWS Cloudfront CDN, чтобы оптимизировать скорость загрузки.
1*7qRL-fNyFh7eNe4KbIPn3Q

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

На сервере приложений не используется дисковое пространство или вычислительная мощность, поскольку все хранится на S3 и обрабатывается Lambda.

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

Пошаговый учебник

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

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

Шаг 1: Создайте тему на AWS SNS

Прежде всего, нам нужно настроить новую тему SNS (Simple Notification Service), по которой AWS будет публиковать сообщения всякий раз, когда новое изображение загружается на S3. Это сообщение содержит ключ объекта S3, позже используемый функцией Lambda для получения загруженного изображения и его обработки.

Из консоли AWS перейдите на страницу SNS, нажмите «Создать тему» ​​и введите название темы, например, «предварительная обработка изображений».

1*b5URVdeHEjEh9Upi08tIqQ

Далее нам нужно изменить политику темы, чтобы разрешить нашему сегменту S3 публиковать сообщения в нем.

На странице темы щелкните Действия -> Редактировать политику темы, выберите Расширенный вид, добавьте следующий блок JSON (с вашими собственными арнами для Resource и SourceArn) в массив операторов и обновите политику:

{      "Sid": "ALLOW_S3_BUCKET_AS_PUBLISHER",      "Effect": "Allow",      "Principal": {        "AWS": "*"      },      "Action": [        "SNS:Publish",      ],      "Resource": "arn:aws:sns:us-east-1:AWS-OWNER-ID:image-preprocessing",      "Condition": {          "StringLike": {              "aws:SourceArn": "arn:aws:s3:*:*:YOUR-BUCKET-NAME"          }      }}

Вы можете найти пример полной политики JSON здесь.

Шаг 2: Создайте структуру папок AWS S3

Теперь нам нужно подготовить структуру папок на S3, которая будет содержать оригинальные и обработанные изображения. В этом примере мы создадим две версии изображения с измененным размером: 800×600 и 400×300.

На консоли AWS откройте страницу S3 и создайте новое ведро. Я назову свой «пример предварительной обработки изображения». Затем внутри ведра нам нужно создать папку с названием «оригиналы», одну папку с названием «800×600» и еще одну папку с названием «400×300».

1*H8zhYpEEHofH67mjPI01lA

Шаг 3: Настройте события AWS S3

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

Для этого откройте сегмент S3 на консоли AWS, нажмите «Свойства» -> «События» -> + Добавить уведомления и заполните следующие поля:

1*O3q00njD2ruHfX1zldXnSg

Здесь мы сообщаем S3 генерировать событие каждый раз, когда создается новый объект (ObjectCreate) в папке оригиналов (префикс) и публиковать это событие в нашей теме SNS «предварительная обработка изображений».

Шаг 4. Настройте роль IAM, чтобы позволить Lambda получить доступ к папке S3

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

На странице IAM консоли AWS:

1. Нажмите кнопку «Создать политику».
2. Нажмите JSON и введите (замените YOUR-BUCKET-NAME)

{      "Version": "2012-10-17",      "Statement": [          {              "Sid": "Stmt1495470082000",              "Effect": "Allow",              "Action": [                  "s3:*"              ],              "Resource": [                  "arn:aws:s3:::YOUR-BUCKET-NAME/*"              ]          }      ]}

где ресурс – наше ведро на S3. Нажмите Обзор, введите имя политики, например AllowAccessOnYourBucketName, и создайте политику.

1*gZcfUJozRyZMTLZPSd8TPw

3. Нажмите Роли -> Создать роль
4. Выберите Aws Service -> Lambda (кто будет использовать политику)
5. Выберите предварительно созданную политику (AllowAccessOnYourBucketName)
6. Нажмите «Обзор», введите имя (LambdaS3YourBucketName) и нажмите «Создать роль».

1*tCw83JM8xraEa0P2ZXhIaQ
Создание лямбда-роли
1*0RL2XQ5xaayD5wwB3eVonw
Присоединить политику к лямбда-роли
1*Jtm_1s64mpvRoRO7NlNkdQ
Сохранить роль

Шаг 5: Создайте функцию AWS Lambda

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

Начнём с создания новой функции Lambda.

На консоли AWS перейдите на страницу Lambda, нажмите «Создать функцию» и введите имя функции, например ImageResize, выберите среду выполнения, в этом случае Node.js 6.10, и созданную роль IAM.

1*mmWNnZa-eWH-hs9oJ6LhLg

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

Для этого нажмите SNS в списке триггеров, выберите image-processing в списке тем SNS и нажмите add.

1*0aZu0bxIsYjyiEdHGPT3og

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

Вы можете скачать код здесь. Единственный файл, который нужно загрузить в вашу функцию Lambda, – это версия 1.1.zip, содержащая index.js и папку node_modules.

1*F4um-2CraoefdiLYcZ0ofg

Для предоставления функции Lambda достаточно времени и памяти для обработки изображения мы можем увеличить память до 256 МБ и время ожидания до 10 секунд. Необходимые ресурсы зависят от размера изображения и сложности трансформации.

1*a8x2lFcEgOxXwug-AGY-_A

Сам код достаточно прост и преследует цель лишь продемонстрировать интеграцию с AWS.

Первоначально определяется функция обработки (exports.handler). Эту функцию вызывает внешний триггер, в этом случае сообщение, опубликованное в SNS, содержащее ключ объекта S3 загруженного изображения.

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

После того, как оно имеет ведро и ключ объекта, загруженное изображение получается с помощью s3.getObject а затем передается в функцию изменения размера. The РАЗМЕР переменная содержит размеры изображений, которые мы хотим создать, также соответствующие названиям папок S3, куда будут загружены трансформированные изображения.

var async = require('async');var AWS = require('aws-sdk');var gm = require('gm').subClass({ imageMagick: true });var s3 = new AWS.S3();
var SIZES = ["800x600", "400x300"];
exports.handler = function(event, context) {    var message, srcKey, dstKey, srcBucket, dstBucket, filename;    message = JSON.parse(event.Records[0].Sns.Message).Records[0];
srcBucket = message.s3.bucket.name;    dstBucket = srcBucket;    srcKey    =  message.s3.object.key.replace(/\+/g, " ");     filename = srcKey.split("/")[1];    dstKey = "";     ...    ...    // Download the image from S3    s3.getObject({            Bucket: srcBucket,            Key: srcKey    }, function(err, response){        if (err){            var err_message="Cannot download image: " + srcKey;            return console.error(err_message);        }        var contentType = response.ContentType;
        // Pass in our image to ImageMagick        var original = gm(response.Body);
        // Obtain the size of the image        original.size(function(err, size){            if(err){                return console.error(err);            }
            // For each SIZES, call the resize function            async.each(SIZES, function (width_height,  callback) {                var filename = srcKey.split("/")[1];                var thumbDstKey = width_height +"/" + filename;                resize(size, width_height, imageType, original,                          srcKey, dstBucket, thumbDstKey, contentType,                        callback);            },            function (err) {                if (err) {                    var err_message="Cannot resize " + srcKey;                    console.error(err_message);                }                context.done();            });        });    });
}

Функция изменения размера применяет некоторые трансформации к оригинальному изображению с помощью библиотеки gm, в частности она изменяет размер изображения, обрезает его при необходимости и снижает качество до 80%. Затем он загружает измененное изображение в S3 с помощью «s3.putObject«, указав»ACL: общедоступный”, чтобы сделать новое изображение общедоступным.

var resize = function(size, width_height, imageType,                       original, srcKey, dstBucket, dstKey,                       contentType, done) {
    async.waterfall([        function transform(next) {            var width_height_values = width_height.split("x");            var width  = width_height_values[0];            var height = width_height_values[1];
            // Transform the image buffer in memory            original.interlace("Plane")                .quality(80)                .resize(width, height, '^')                .gravity('Center')                .crop(width, height)                .toBuffer(imageType, function(err, buffer) {                if (err) {                    next(err);                } else {                    next(null, buffer);                }            });        },        function upload(data, next) {            console.log("Uploading data to " + dstKey);            s3.putObject({                    Bucket: dstBucket,                    Key: dstKey,                    Body: data,                    ContentType: contentType,                    ACL: 'public-read'                },                next);            }        ], function (err) {            if (err) {                console.error(err);            }            done(err);        }    );};

Шаг 6: Тест

Теперь мы можем проверить, работает ли все правильно, загрузив изображение в папку оригиналов. Если все реализовано правильно, мы должны найти измененную версию загруженного изображения в папке 800×600 и одну в папке 400×300.

На видео ниже можно увидеть три окна: слева папка оригиналов, посередине папка 800×600, а справа папка 400×300. После загрузки файла в исходную папку обновляются два других окна, чтобы проверить, создано ли изображение.

И вуаля, вот они 😉

(Необязательно) Шаг 6: Добавьте Cloudfront CDN

Теперь, когда изображение создано и загружено в S3, мы можем добавить Cloudfront CDN для доставки изображений нашим конечным пользователям, чтобы улучшить скорость загрузки.

  1. Откройте страницу Cloudfront
  2. Нажмите кнопку «Создать распространение»
  3. Когда вас спросят о способе доставки, выберите «Веб-распространение»
  4. Выберите сегмент S3 как «Начальное доменное имя» и нажмите кнопку «Создать распространение».

Процесс создания сети распространения не является мгновенным, поэтому вам придется подождать, пока статус вашего CDN изменится с «В Прог«до»Развернуто.»

После развертывания можно использовать имя домена вместо URL-адреса сегмента S3. Например, если ваше доменное имя Cloudfront1234-cloudfront-id.cloudfront.net«, тогда вы сможете получить доступ к папке с измененным размером изображений с помощью «https://1234-cloudfront-id.cloudfront.net/400×300/ИМЯ ФАЙЛА» и «https://1234-cloudfront-id.cloudfront.net/800×600/ИМЯ ФАЙЛА»

1*nsIx2cOXyOLkyKEJGRE9lQ

Cloudfront имеет множество других параметров, которые необходимо настроить, но они выходят за рамки этой статьи. Дополнительные инструкции по настройке CDN см. в руководстве с начала работы Amazon.

И все это! Надеюсь, вам понравилась эта статья. Пожалуйста, оставьте комментарий ниже и дайте мне знать, что вы думаете!

Добавить комментарий

Ваш адрес email не будет опубликован.