пятница, 7 апреля 2017 г.

[prog.c++] Тема movable/modifiable сообщений в SObjectizer задышала

В конце февраля, после C++ Russia 2017, возникла тема добавления в SObjectizer поддержки не константных экземпляров сообщений. Суть в том, что в SObjectizer из-за первоначальной направленности на взаимодействие в режиме 1:N, все сообщения доставлялись получателям в виде константных объектов. Получатель имел константную ссылку на пришедшее к нему сообщение и не должен был менять этот объект, т.к. в это же время то же самое сообщение мог обрабатывать еще кто-то.

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

В итоге мы начали думать, как дать возможность пересылать не константные объекты-сообщения при взаимодействии агентов в режиме 1:1. Сначала идеи крутились вокруг movable-сообщений, т.е. сообщений, содержимое которых можно перемещать между экземплярами (например, когда кто-то делает send, то содержимое сообщение сразу же конструируется где-то в заранее созданном буфере у агента-получателя). Но затем была выбрана, как мне представляется, более простая идея: мы применили к сообщениям тот же подход, который в C++ используется для динамически-созданных объектов и unique_ptr. Класс unique_ptr говорит разработчику, что есть только один "владеющий" указатель на объект. Нельзя расплодить unique_ptr, можно лишь передавать право владения так, что только один unique_ptr будет владеть динамически созданным объектом.

Мы добавили в SObjectizer такое понятие, как unique_mbox. Т.е. специальный тип mbox-а, отсылка сообщения в который приводит к передаче не константного объекта-сообщения. Для того, чтобы получить не константное сообщение из unique_mbox-а, обработчик сообщения должен использовать специальный новый тип unique_mhood_t<Msg>. Это тонкая шаблонная обертка, похожая на уже существующий mhood_t, но она позволяет модифицировать полученный объект-сообщение. Так же unique_mhood_t позволяет переслать не константное сообщение дальше.

Вот небольшой кусок реального unit-теста из SObjectizer, который проверяет работу этого механизма. Там агент отправляет самому себе сообщение типа std::string. Получив сообщение, агент меняет текст сообщения и перепосылает измененный объект. При этом проверяется то, что повторно приходит именно тот же самый std::string, но с другим содержимым.

среда, 5 апреля 2017 г.

[prog.c++] Альфа-версия RESTinio, инструмента для реализации REST API на C++

Мы долго смотрели по сторонам на то, что есть в C++ для разработки HTTP-сервисов и REST-сервисов в частности. И решили все-таки попробовать сделать что-то свое. Попробовали. Сегодня выкатываем в мир первую альфа-версию небольшого, header-only, кросс-платформенного инструмента под названием RESTinio. Его главная задача -- упростить асинхронную обработку запросов. Чтобы, грубо говоря, обработчик спокойно мог потратить 15 секунд на формирование ответа, но это бы не влияло на параллельные запросы.

В основе лежит Asio и http_parser из NodeJS. SObjectizer для работы RESTinio не нужен. Но обработка запросов легко может делегироваться из RESTinio в SObjectizer. Пример того, как это делается можно найти в репозитории.

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

Ну и это еще одно подтверждение того, что мы уже несколько раз говорили на конференциях в статьях: мы больше смотрим в сторону того, чтобы дать возможность агентам общаться с внешним миром посредством де-факто стандартных протоколов, вроде HTTP ;) При этом планов привязать RESTinio намертво к SObjectizer-у у нас нет. Тут все просто: из SObjectizer должно быть удобно пользоваться RESTinio, но самому RESTinio SObjectizer для работы не нужен.

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

воскресенье, 2 апреля 2017 г.

[prog.c++] Аннотированный код нового примера для SObjectizer-5.5.19

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

Обычный SObjectizer Environment для выполнения своей работы запускает три дополнительные рабочие нити. Первая принадлежит диспетчеру по умолчанию (к этому диспетчеру агенты привязываются, если пользователь явно не указал, на каком диспетчере агент должен работать). Вторая обслуживает таймер (она так и называется -- таймерная нить). Третья нить нужна для того, чтобы корректно завершать дерегистрацию коопераций и давать возможность диспетчерам завершать рабочие нити, созданные под обслуживание этих коопераций.

Эти три дополнительные нити не являются сколько-нибудь существенными накладными расходами в большом многопоточном приложении, в котором создаются десятки, а то и сотни рабочих потоков. Но бываю случаи, когда SObjectizer используется в небольших утилитах, в которых практически всю работу можно делать на контексте одной-единственной рабочей нити. В таких случаях создание сразу трех(!) дополнительных нитей -- это из пушки по воробьям.

Как раз для поддержки подобных случаев в SO-5.5.19 добавляется возможность создать такой SObjectizer Environment, который будет все свои действия выполнять на одной-единственной нити. Т.е. диспетчер по умолчанию, таймер и окончательная дерегистрация будут работать на той самой нити, на которой был произведен вызов so_5::launch().

Фича эта уже работает, но мы пока еще не проверили ее во всех сценариях, для которых она предназначалась. Поэтому пока не можем считать ее готовой к релизу. Но в процессе ее реализации в список примеров SObjectizer-а был добавлен еще один пример. Мне кажется, он достаточно интересен и демонстрирует использование многопоточности вообще без агентов. Только голые нити и CSP-шные каналы (называющиеся у нас mchain-ы). Ну и очередная разновидность ping-pong-а, только на этот раз с использованием таймеров. Пример, снабженный комментариями, под катом. Кому интересно, милости прошу посмотреть и высказать свое фи авторитетное мнение.

Напоследок скажу, что в скоуп версии 5.5.19 входит еще одна важная фича -- это moveable-сообщения, о которых речь заходила с месяц назад. Работы здесь еще очень много. Так что пока даже не рискну предположить предполагаемую дату релиза. Хорошо бы успеть к концу апреля, но загадывать не берусь. Жаль, что работы над 5.5.19 так затянулись, но однопоточный SObjectizer Environment -- это настолько переворачивающая все с ног на голову переделка SO-5, что торопиться ну очень не хочется.

Итак, сам код: