C++ SDK для Amazon Web Services

6092e8ab6c5f4375a35c8910fe670126.png
Под конец прошлого года компания Amazon выпустила (наконец-то!) C++ SDK для своей платформы AWS. Непонятно, почему так долго тянули — есть масса приложений, написанных на С++, из которых давно хотелось удобно пользоваться AWS. Не джавой же единой, в самом деле.

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

С++11


Как в своё время должен был быть разрушен Карфаген, так сегодня должны умереть все библиотеки и приложения, до сих пор не перешедшие на С++11. Им просто не место в современном мире. Со слезами на глазах я читаю посты вроде этого о библиотеках, которые «чтобы расширить аудиторию — стараются обходиться без C++11». Как по мне, то это решение где-то около «будем ездить на лошадях, потому что они привычнее автомобилей». Новое SDK от Amazon не гонится за ретроградами — из коробки нам предлагаются все фичи С++11: auto, умные указатели, лямбды и т.д. Дать возможность указать в виде колбэк-функции лямбду — жизненно необходимо, без этого нас даже Javascript засмеёт. Ну и никаких new\delete в явном виде, конечно. Красота!

Простота кода


Как результат предыдущего пункта — посмотрите только на код, который получается в результате. Положим в DynamoDB пару «ключ-значение»:

DynamoDBClient client;
AttributeValue haskKeyAttribute;
hashKeyAttribute.SetS("SampleHashKeyValue")
AttributeValue valueAttribute;
valueAttribute.SetS("SampleValue")

PutItemRequest putItemRequest;
putItemRequest.WithTableName("TestTableName").AddItem("HashKey", hashKeyAttribute).AddItem("Value", valueAttribute);

client.PutItem(putItemRequest);

Ну вот и скажите мне, любители выражений вроде «С++ слишком сложен и избыточен» — чем это хуже Java или Python? А памяти съест меньше и выполнится быстрее.

Нет лишних зависимостей


Ребята из Амазона понимают, что каждая лишняя зависимость порождает дополнительную сложность при подключении и сборке проекта, а значит уменьшает количество пользователей. Поэтому AWS C++ SDK не зависит ни от boost, ни от других фундаментальных фреймворков. Даже маленьких библиотек, вроде логирования, в нём нет. Единственная зависимость — HTTP клиент. Всё-таки запихивать в SDK собственную реализацию этого дела было бы уже чересчур. Из коробки предлагается на выбор привязка к системным CURL, WinHTTP, WinInet или возможность внедрить свою реализацию.

CMake


Мир С++ по-маленьку движется к стандартизации описания проектов. CMake может быть ещё пока и не победил окончательно, но у тех, кому нужно дать возможность сборки под как можно более широкий набор платформ и компиляторов, этот вариант приходит на ум первым. Не отличился тут и Amazon — всё в CMake и компилируйте себе на чём угодно. Очень удобно работать в CLion.

Dependency Injection


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

Логирование


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

Синхронные и асинхронные версии всех операций


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

CreateTableOutcome createTableOutcome = dynamoDbClient->CreateTable(createTableRequest);
  if (createTableOutcome.IsSuccess())
  {
...
  } else {
...
  }

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

void putItemFinished(const DynamoDBClient *client, const PutItemRequest& request, 
                                const PutItemOutcome& outcome,
                                const std::shared_ptr& context)
{
 if (outcome.IsSuccess())
 {
        SEND_QUEUE.erase(context->GetUUID());
 }
}

auto context = Aws::MakeShared("Hello!");
context.SetUUID("UniqueRequestKey");
SEND_QUEUE["UniqueRequestKey"] = putItemRequest;
client.PutItemAsync(putItemRequest, &putItemFinished, context);

Снова слава С++11: в качестве колбэка можно передавать не только функцию, но и метод класса некоторого объекта (через std: bind) или лямбду.

Поддержка Asynchronous executor


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

Ограничение скорости


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

Нет исключений


Вопрос использования или неиспользования исключений — давний холивар в мире С++. Но во всё большем количестве примеров в последнее время исключений стараются избегать. В AWS C++ SDK исключений тоже нет. Аргументируют большей гибкостью и производительностью. Нужен код ошибки — проверяйте outcome.IsSuccess (), если там false — в outcome будут также дополнительные данные об ошибке. Не знаю как вам, а мне такой подход нравится.

Включенная по умолчанию стратегия повтора попыток выполнения операции


Поскольку любая SDK от Amazon — это в первую очередь история о сетевых операциях, то нужно понимать, что сетевые операции частенько заканчиваются ошибками. Где-то Wi-Fi подвёл, где-то мобильная сеть, а где-то вроде бы стабильный наземный канал почему-то отказался загружать смешной трёхтеррабайтный файл. AWS C++ SDK по-умолчанию пытается повторить закончившуюся ошибкой операцию. Детали алгоритма (количество попыток повтора, время между попытками) можно контролировать собственной реализацией стратегии повторов.

Возможность определения аллокаторов памяти


Если для вас важно, сколько памяти использует ваше приложение, когда оно её выделяет и когда удаляет, как быстро это происходит (а для вас это всё должно быть интересно, зачем иначе вы вообще пишете на С++?) — вы можете определить собственный аллокатор памяти. Для этого нужно унаследоваться от MemorySystemInterface и определить несколько методов. Можно попробовать стянуть откуда-нибудь хороший аллокатор, заточить его под свои объёмы данных и алгоритмы и получить неплохой прирост производительности.

GitHub


Всё живёт на GitHub, принимаются Issues, Pull Requests. Контакт с сообществом поставлен хорошо.

Удачи в использовании AWS из приложений на С++!

© Habrahabr.ru