среда, 29 ноября 2017 г.

[prog.c++] Смотрим в сторону SObjectizer-5.6 (самое первое приближение)

Прошло уже более трех лет с момента релиза версии 5.5.0. Все эти три года SObjectizer развивался в рамках ветки 5.5 с особым акцентом на совместимость между версиями. Может быть не всегда у нас получалось сохранять 100% совместимость и где-то что-то могло поломаться. Но, в подавляющем большинстве случаев, насколько мне известно, переход на новые версии SO-5.5.* происходил без каких-либо сложностей и необходимости что-то исправлять в коде.

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

Имхо, три года развития в рамках ветки 5.5 -- это вполне достаточный срок для того, чтобы задуматься о том, чтобы заняться версией 5.6, в которой уже не будет 100% обратной совместимости с SObjectizer-5.5. И переход на которую будет требовать модификации исходников, а может быть, местами, и перепроектирования. Посему под катом изложено несколько текущих мыслей на тему версии 5.6. Кому интересно -- милости прошу. Чем больше будет фидбэка, тем лучше.


Первый момент связан с предполагаемой датой релиза версии 5.6.0 и привязкой к стандарту C++, а так же к минимальным версиям основных C++ компиляторов, на которые мы будем ориентироваться (в первую очередь это gcc, clang и vc++).

С версией 5.5 получилось достаточно просто. В один прекрасный момент мы приняли решение, что версия 5.5 будет завязана на gcc из Ubuntu 14.04 LTS. Т.е. на gcc-4.8. Соответственно, со стороны vc++ -- это vc++12 (из vs2013). Ну и clang 3.4. Соответственно, до окончания времени жизни Ubuntu 14.04 LTS (т.е. до апреля 2019-го года) ветка 5.5 будет требовать gcc-4.8/vc++12/clang-3.4.

Для версии 5.6 логично было бы заложиться на Ubuntu 16.04 LTS. Тогда получится gcc-5.4.1/vc++15(vs2017)/clang-3.8 (закладываться на vc++14 (vs2015) смысла не вижу). А это означает возможность использования многих фич из C++14. Но...

Предположительно версия 5.6 раньше апреля 2018-го не появится. А в апреле 2018 выйдет Ubuntu 18.04 LTS. В которой, думается, основным компилятором будет уже gcc-7.2 (как минимум, а то и gcc-7.3 или даже gcc-8.0). Соответственно, есть соблазн уже сейчас ориентироваться на gcc-7.2/vc++15/clang-5.0. Ну и, соответственно, на C++17. С учетом того, что разработка SObjectizer-5.6 займет какое-то время, да и сам SObjectizer используется не так активно, как нам бы хотелось, выбор C++17 в качестве минимума для SO-5.6 выглядит очень даже привлекательно для нас, как разработчиков SObjectizer-а.

Впрочем, здесь очень важно услышать тех, кто более-менее заинтересован в SObjectizer-е. Устроит ли вас, что SO-5.6 будет требовать C++17? Или же лучше ориентироваться на C++14? Или даже на C++11? Хотя оставаться в рамках C++11, имхо, уже не очень разумно. Особенно с учетом того, что SObjectizer -- это бесплатный продукт, который мы развиваем за собственный счет.

Если мы привяжемся к Ubuntu 16.04 LTS, то версия 5.6 будет сопровождаться до окончания жизни 16.04 LTS, т.е. до 2021-го года. Если же привяжемся к Ubuntu 18.04 LTS, то этот срок увеличится до 2023-го.


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

Соответственно, по мере сил и возможностей мы будем заниматься реализацией поддержки распределенных SObjectizer-приложений. Но не за счет внедрения таких возможностей в сам SObjectizer, сколько за счет дополнительных библиотек. Так, у нас в планах включение в so_5_extra специализированных mbox/mchain-ов, которые позволят организовать взаимодействие между независимыми процессами через shared memory. Это не полноценная распределенность, конечно же, но уже большое подспорье для того, чтобы можно было разбить большое, монолитное C++ приложение на несколько процессов. Соответственно, падение и рестарт одного из них не будет сказываться на других процессах. Потом, возможно, мы займемся и другими типами транспорта. Но здесь нужно помнить важную вещь, от которой мы не можем отмахнуться: сейчас недостаточно научить общаться между собой только C++ приложения. Обязательно нужна интероперабильность: С++ должен уметь общаться с компонентом на любом другом языке. Поэтому мы вряд ли будем делать поддержку распределенных приложений, которая заточена только под C++. Если только это не будет нужно под какую-то конкретную и специфическую задачу, в которой по тем или иным причинам ничего, кроме C++, быть не может.


Третий момент связан с поддержкой агентов в виде сопрограмм (т.е. когда агент работает как stackful coroutine). Есть желание попробовать такое сделать. Но вряд ли эта фича будет втянута именно в ядро SObjectizer. Пока я лично больше склоняюсь к тому, чтобы дать возможность вешать хуки на такие вещи, как request_value/request_future. А поддержка именно сопрограмм будет выполняться специализированными диспетчерами, которые будут привязывать агентов не к рабочим нитям (как штатные диспетчеры SO-5), а к сопрограммам. Соответственно, реализации таких диспетчеров будут представлены сторонними библиотеками. Скажем, какая-то библиотека на базе Boost.Fibers.

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


Еще одна вещь, достаточно неожиданная лично для меня, но которая, видимо, становится результатом эволюции SObjectizer-а. Это возрастающая роль mbox-ов. И увеличивающееся их многообразие.

Так, начинался SO-5 вообще с единственного типа mbox-а: Multi-Producer/Multi-Consumer. Затем добавился Multi-Producer/Single-Consumer. Затем появились mchain-ы, которые могут притворяться mbox-ами. Затем появились эксперименты с упрятыванием за mbox-ами всяких дополнительных сущностей. Один пример -- это уже означенный выше mosquitto_transport, в котором mbox-ы используются для публикации и для получения сообщений от MQTT-брокера. Еще один пример -- это компонент shutdowner из so_5_extra.

Дальше -- больше. В so_5_extra уже добавлены новые типы "хитрых" mbox-ов: round-robin и collecting. И прямо сейчас добавляется еще один, retained_msg (сохраняет последний отосланный экземпляр и автоматически отсылает его каждому новому подписчику, тем самым подписчик автоматически получает последнее отосланное в mbox сообщение). И, есть ощущение, что это не предел.

Уже несколько раз меня посещали мысли о том, что было бы хорошо сделать композицию mbox-ов. Например, чтобы можно было выстроить цепочку из collecting_mbox+round_robin+retained_msg. В рамках интерфейса mbox-ов в SO-5.5 это не получается сделать. А вот в SO-5.6 интерфейс mbox-а и так будет модифицирован, поэтому в SO-5.6 можно будет поддержать и каскадирование mbox-ов.

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


Еще один момент, которому хотелось бы уделить внимание в SO-5.6. Это упрощение реализации "цепочечных" операций. Т.е. когда агент A отсылает агенту B какое-то сообщение, агент B должен его обработать, после чего агент A должен среагировать на результат этой обработки. Если использовать синхронное взаимодействие, то такие операции записываются достаточно тривиально:

class B : public so_5::agent_t {
   ...
public:
   struct request {...};
   struct response {...};
   ...
   response on_request(mhood_t<request> cmd) {
      ...
      return {...};
   }
};

class A : public so_5::agent_t {
   ...
   void do_somethig(const so_5::mbox_t & b) {
      auto resp = so_5::request_value<B::response, B::request>(b, so_5::infinite_wait, ...);
      ...
   }
};

Но синхронное взаимодействие требует, чтобы A и B работали на разных контекстах. Не всегда это так. И, если это не так, приходится использовать пинг-понг сообщениями между агентами:

class B : public so_5::agent_t {
   ...
public:
   struct request {
      const so_5::mbox_t reply_to_;
      ...
   };
   struct response {...};
   ...
   void on_request(mhood_t<request> cmd) {
      ...
      so_5::send<response>(cmd->reply_to_, ...);
   }
};

class A : public so_5::agent_t {
   ...
   void so_define_agent() override {
      so_subscribe_self().event(&A::on_response);
      ...
   }

   void do_somethig(const so_5::mbox_t & b) {
      so_5::send<B::request>(b, ...);
   }

   void on_response(mhood_t<B::response> cmd) {
      ...
   }
};

Обычный подход, но местами слишком уж муторный. Иногда хочется иметь возможность увязать операции в цепочку (по аналогии с тем, как работают future.then в некоторых языках программирования). Например, сделать что-то такое:

class B : public so_5::agent_t {
   ...
public:
   struct request {...};
   struct response {...};
   ...
   response on_request(mhood_t<request> cmd) {
      ...
      return {...};
   }
};

class A : public so_5::agent_t {
   ...
   void do_somethig(const so_5::mbox_t & b) {
      so_5::send_and<B::request>(b, ...)
         .then([this](mhood_t<B::response> cmd) {
            ...
         });
   }
};

Т.е. фокус здесь в том, что SObjectizer доставляет B::request до агента B как обычное сообщение. После чего берет возвращенное агентом B значение (т.е. экземпляр B::response) и доставляет это значение как невидимое сообщение до агента A. И вызывает назначенный в then() обработчик B::response на рабочем контексте агента A.

Имхо, это может оказаться интересной и полезной штукой. Причем, есть подозрение, что ее можно будет сделать и не дожидаясь SO-5.6. Так что, если кому-то интересно что-то подобное, то дайте знать. Может быть под это дело мы зарядим SO-5.5.21. Или даже успеем впихнуть в SO-5.5.20 (хотя вряд ли, но это будет зависеть от ваших голосов).


В завершении скажу пару слов о документации. Как раз в версии 5.5 мы смогли довести объем документации для SObjectizer-а на приемлемый, по моему мнению, уровень. Есть еще над чем работать и мы будем еще над этим работать. Соответственно, весь этот объем будет адаптирован затем под версию 5.6. А это так же весьма приличный объем работы. Что не сможет не сказаться на сроках разработки SO-5.6.

Вот с чем пока серьезная засада, так это с перечислением success story. Дело в том, что наш бывший работодатель пока не горит желанием позволить нам рассказать, как SObjectizer использовался на практике. Что, в принципе, можно понять, т.к. SObjectizer там задействовался в инфраструктуре по обслуживанию важных сервисов крупных банков и мобильных операторов. Но нам от этого не легче :( Дабы ни с кем не портить отношения мы не рискуем публиковать такую информацию самостоятельно.

Нам известно, что SObjectizer то тут, то там используется для прототипирования. Без нашего участия, люди сами находят SO-5, берут и пробуют. Иногда спрашивают совета или пояснений, иногда обходятся своими силами. Но точно есть проекты/проектики, которые делаются на SObjectizer-е и не нами :) Так что остается только подождать, пока информация о результатах этих экспериментов и прототипов станет публично доступной. Тогда мы сможем перейти и к формированию раздела success stories. Но до тех пор остается только верить нам на слово: SObjectizer используется, приносит кому-то пользу. Вот не врем, чесслово. ;)


В общем, вот такие пока размышления на тему SO-5.6. Если кто-то хочет высказать свое мнение/замечание/пожелание -- то мы с удовольствием выслушаем, если это конструктивное мнение. Можно сделать это здесь, в комментариях (хотя G+ и blogger иногда теряют комментарии или не сообщают вовремя о них). Можно написать на наш официальный ящик: info на stiffstream точка com. Можно напрямую мне: eao197 на gmail точка com.

Кстати говоря, сейчас SObjectizer заточен под C++. Но если кому-то нужен подобный инструмент для другого языка, скажем, для Rust-а, и есть деньги на подобную разработку, то почему бы и нет? ;)

Комментариев нет: