Pull to refresh
86
1
Евгений Охотников @eao197

Велосипедостроитель, программист-камикадзе

Send message

Когда акторный фреймворк превращается в «черный ящик» и что мы можем с этим сделать?

Reading time16 min
Views3.3K
Модель акторов — это хороший подход к решению некоторых типов задач. Готовый акторный фреймворк, особенно в случае языка C++, может очень сильно облегчить жизнь разработчика. С программиста снимается изрядная часть забот по управлению рабочими контекстами, организации очередей сообщений, контролю за временем жизни сообщений и т.д. Но, как говорится, все хорошее в этой жизни либо противозаконно, либо аморально, либо ведет к ожирению ничего не дается бесплатно. Одна из проблем использования готового (т.е. чужого) акторного фреймворка состоит в том, что иногда он превращается в «черный ящик». Ты видишь, что ты отдаешь в этот «черный ящик», ты видишь, что из него приходит (если вообще приходит). Но далеко не всегда понятно, как из первого получается второе…
Читать дальше →
Total votes 14: ↑14 and ↓0+14
Comments1

Асинхронные HTTP-запросы на C++: входящие через RESTinio, исходящие через libcurl. Часть 3

Reading time23 min
Views3.8K
В предыдущей статье мы разобрали реализацию двухпоточного bridge_server-а. На одном потоке асинхронно обрабатываются входящие HTTP-запросы посредством RESTinio. На втором потоке выполняются асинхронные запросы к delay_server-у посредством libcurl в виде curl_multi с использованием функций curl_multi_perform и curl_multi_wait.

Сегодня же мы разберем другую реализацию bridge_server-а, которая асинхронно обслуживает и входящие, и исходящие HTTP-запросы на одном и том же пуле потоков. Из libcurl-а для этих целей применяется функция curl_multi_socket_action.

Эта реализация заняла у нас больше всего времени. Давно не приходилось выкуривать столько бамбука, сколько довелось при разбирательстве с документацией к этой функции и примерами ее использования. По началу все это вообще воспринималось как какая-то черная магия, но потом свет в конце туннеля все-таки забрезжил, а код — заработал. Как именно заработал? А вот об этом сегодня и поговорим.
Читать дальше →
Total votes 14: ↑14 and ↓0+14
Comments7

Асинхронные HTTP-запросы на C++: входящие через RESTinio, исходящие через libcurl. Часть 2

Reading time22 min
Views9.2K
В предыдущей статье мы начали рассказывать о том, как можно реализовать асинхронную обработку входящих HTTP-запросов, внутри которой нужно выполнять асинхронные исходящие HTTP-запросы. Мы рассмотрели реализованную на C++ и RESTinio имитацию стороннего сервера, который долго отвечает на HTTP-запросы. Сейчас же мы поговорим о том, как можно реализовать выдачу асинхронных исходящих HTTP-запросов к этому серверу посредством curl_multi_perform.

Несколько слов о том, как можно использовать curl_multi


Библиотека libcurl широко известна в мире C и C++. Но, вероятно, наиболее широко она известна в виде т.н. curl_easy. Использовать curl_easy просто: сперва вызываем curl_easy_init, затем несколько раз вызываем curl_easy_setopt, затем один раз curl_easy_perform. И, в общем-то, все.

В контексте нашего рассказа с curl_easy плохо то, что это синхронный интерфейс. Т.е. каждый вызов curl_easy_perform блокирует вызвавшую его рабочую нить до завершения выполнения запроса. Что нам категорически не подходит, т.к. мы не хотим блокировать свои рабочие нити на то время, пока медленный сторонний сервер соизволит нам ответить. От libcurl-а нам нужна асинхронная работа с HTTP-запросами.

И libcurl позволяет работать с HTTP-запросами асинхронно через т.н. curl_multi. При использовании curl_multi программист все так же вызывает curl_easy_init и curl_easy_setopt для подготовки каждого своего HTTP-запроса. Но не делает вызов curl_easy_perform. Вместо этого пользователь создает экземпляр curl_multi через вызов curl_multi_init. Затем добавляет в этот curl_multi-экземпляр подготовленные curl_easy-экземпляры через curl_multi_add_handle и…
Читать дальше →
Total votes 18: ↑17 and ↓1+16
Comments6

Асинхронные HTTP-запросы на C++: входящие через RESTinio, исходящие через libcurl. Часть 1

Reading time16 min
Views9.6K

Преамбула


Наша команда занимается разработкой небольшого, удобного в использовании, встраиваемого, асинхронного HTTP-сервера для современного C++ под названием RESTinio. Начали его делать потому, что нужна была именно асинхронная обработка входящих HTTP-запросов, а ничего готового, чтобы нам понравилось, не нашлось. Как показывает жизнь, асинхронная обработка HTTP-запросов в C++ приложениях нужна не только нам. Давеча на связь вышли разработчики из одной компании с вопросом о том, можно ли как-то подружить асинхронную обработку входящих запросов в RESTinio с выдачей асинхронных исходящих запросов посредством libcurl.

По мере выяснения ситуации мы обнаружили, что эта компания столкнулась с условиями, с которыми сталкивались и мы сами, и из-за которых мы и занялись разработкой RESTinio. Суть в том, что написанное на C++ приложение принимает входящий HTTP-запрос. В процессе обработки запроса приложению нужно обратиться к стороннему серверу. Этот сервер может отвечать довольно долго. Скажем, 10 секунд (хотя 10 секунд — это еще хорошо). Если делать синхронный запрос к стороннему серверу, то блокируется рабочая нить, на которой выполняется HTTP-запрос. А это начинает ограничивать количество параллельных запросов, которые может обслуживать приложение.
Читать дальше →
Total votes 16: ↑16 and ↓0+16
Comments0

Задействовать для простых тестов наследование, полиморфизм и шаблоны? Почему бы и нет…

Reading time15 min
Views8.5K
Язык C++ сложен. Но его сложность проистекает из сложности задач, которые решаются с помощью C++. Каждая фича, которая была добавлена в C++, была добавлена не просто так, а для того, чтобы дать возможность справиться к какой-то проблемой. Ну а уж сочетание существующих в C++ фич делает язык чрезвычайно мощным инструментов. Конкретному примеру того, как это происходит на практике, и посвящена данная статья.

Добавлю еще, что одним из мощных стимулов к написанию данной статьи стало то, что очень часто на глаза попадаются объемные флеймыобсуждения на тему «ООП не нужно» и, особенно, «шаблоны-дженерики на практике почти никогда не нужны». Мне, как далеко не молодому программисту, начинавшему в 1990-ом как раз с инструментов, в которых не было ни ООП, ни шаблонов-дженериков, странно сталкиваться с подобными точками зрения. Но, чем дальше, тем чаще с ними сталкиваешься. Особенно со стороны приверженцев новых языков программирования, вроде Go или Rust-а.

Сложно сказать, чем это вызвано. Может быть людей перекормили ООП (а это так и было)… Может быть задачи за несколько минувших десятилетий сильно поменялись (а это так и есть)… Может быть и просто «вот и выросло поколение»… Как бы то ни было, можно попробовать на примере из реальной жизни показать, что все не так однозначно ©.

Итак, о чем пойдет речь?
Читать дальше →
Total votes 22: ↑16 and ↓6+10
Comments34

Научиться перехватывать необработанные сообщения или пример того, как SObjectizer обрастает новыми фичами…

Reading time13 min
Views2K
Нам очень приятно, когда в SObjectizer добавляются новые возможности, возникшие в результате подсказок и/или пожеланий пользователей SObjectizer-а. Хотя далеко не всегда это оказывается просто. Ведь, с одной стороны, у нас, как у команды разработчиков и старых пользователей SObjectizer-а, уже есть собственные стереотипы о том, как SObjectizer принято использовать. И не всегда получается сразу оценить «свежий взгляд со стороны», понять что реально хочет видеть пользователь во фреймворке и почему он не удовлетворен имеющимися средствами. С другой стороны, SObjectzer не такой уж и маленький фреймворк, добавление новых возможностей требует определенной осмотрительности. Нужно, чтобы новая функциональность не конфликтовала с уже имеющимися фичами. И, тем более, чтобы после добавления чего-то нового не сломалось то, что уже есть и давно работает. Плюс к тому, у нас есть пунктик по поводу сохранения совместимости между версиями SObjectizer-а, поэтому мы сильно против кардинальных изменений…

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

Под катом небольшой рассказ о том, как в SObjectizer добавлялась одна новая фича. Может быть кому-то из читателей будет интересно посмотреть, как старый фреймворк адаптируется под запросы новых пользователей.
Читать дальше →
Total votes 8: ↑8 and ↓0+8
Comments2

Трехэтажные C++ные шаблоны в реализации встраиваемого асинхронного HTTP-сервера с человеческим лицом

Reading time14 min
Views16K
Наша команда специализируется на C++ проектах. И нам время от времени приходилось создавать HTTP-точки входа в C++ компоненты. Для чего использовались разные инструменты. Тут были и старые-добрые CGI, и различные встраиваемые библиотеки, как сторонние, так и самописные. Все это работало, но всегда оставалось ощущение, что следовало бы делать такие вещи и проще, и быстрее, и производительнее.

В итоге мы решили, что пора прекращать смотреть по сторонам и нужно попробовать сделать что-то свое, с преферансом и куртизанками кроссплатформенностью, асинхронностью, производительностью и человеческим отношением к конечному пользователю. В результате у нас получилась небольшая C++14 библиотека RESTinio, которая позволяет запустить HTTP-сервер внутри C++ приложения всего несколькими строчками кода. Вот, например, простейший сервер, который на все запросы отвечает «Hello, World»:

#include <restinio/all.hpp>

int main()
{
   restinio::run(
      restinio::on_this_thread()
         .port(8080)
         .address("localhost")
         .request_handler([](auto req) {
            return req->create_response().set_body("Hello, World!").done();
         }));

   return 0;
}

В реализации RESTinio активно используются C++ные шаблоны и об этом хотелось бы сегодня немного поговорить.
Читать дальше →
Total votes 42: ↑41 and ↓1+40
Comments19

Почтовые ящики, которые и не ящики вовсе…

Reading time19 min
Views5.5K
Когда летом 2016-го года создавалась первая статья про SObjectizer мы говорили, что со временем будем рассказывать и о деталях его реализации, дабы заинтересованные читатели могли заглянуть «под капот». Сегодняшняя статья будет как раз про потроха SObjectizer-а. Про механизм mbox-ов («почтовых ящиков»), который используется для организации взаимодействия акторов (агентов в нашей терминологии).

Почему речь именно про mbox-ы?


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

Зачем нужны mbox-ы?


Почтовые ящики в SObjectizer нужны для того, чтобы организовывать взаимодействие между агентами. Общение между агентами строится посредством асинхронных сообщений и эти самые сообщения нужно куда-то отсылать. Возникает вопрос: «Куда именно?»
Читать дальше →
Total votes 16: ↑16 and ↓0+16
Comments22

Actors for fun and profit

Reading time17 min
Views6.2K

Эта статья является адаптированным вариантом текста одноименного доклада с конференции C++ CoreHard Autumn 2017. Статья рассматривается как завершение темы, поднятой ранее в публикациях "Модель Акторов и C++: что, зачем и как?" и «Шишки, набитые за 15 лет использования акторов в C++» часть I и часть II. Сегодня речь пойдет о том, как понять, что Модель Акторов может быть успешно использована в вашем проекте.


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


Лирическое отступление на тему «Модель Акторов и C++: миф или реальность?»


В статье обсуждаются вещи, которые присущи самой Модели Акторов, безотносительно конкретных языков программирования. Но, т.к. автор тесно связан с разработкой ПО на C++, то есть некоторый акцент на применимость акторов именно в C++.


Читать дальше →
Total votes 8: ↑8 and ↓0+8
Comments0

Многопоточность в C++ и SObjectizer с CSP-шными каналами, но совсем без акторов…

Reading time7 min
Views4.7K

Раньше мы рассказывали про SObjectizer как про акторный фреймворк для C++, хотя в действительности это не совсем так. Например, уже давно в SObjectizer есть такая классная штука, как mchain-ы (они же каналы из модели CSP). Mchain-ы позволяют легко и непринужденно организовать обмен данными между рабочими потоками. Не создавая агентов, которые нужны далеко не всегда. Как раз на днях довелось в очередной раз этой фичей воспользоваться и упростить себе жизнь за счет передачи данных между потоками посредством каналов (т.е. SObjectizer-овских mchain-ов). Так что не только в Go можно получать удовольствие от использования CSP. В C++ это так же возможно. Кому интересно, что и как, прошу под кат.

Читать дальше →
Total votes 17: ↑16 and ↓1+15
Comments4

Объединяем акторов и SEDA-подход: зачем и как?

Reading time16 min
Views4.7K

В одной из статей про шишки, которые довелось набить за 15 лет использования акторов в C++, речь зашла о том, что большое количество акторов — это, зачастую, сама по себе проблема, а отнюдь не решение. И что использование идей из SEDA-подхода может существенно упростить жизнь при разработке приложений на базе модели акторов. Однако, как показали затем вопросы в комментариях к предыдущим статьям, совмещение SEDA-подхода и модели акторов отнюдь не очевидно, поэтому есть смысл копнуть данную тему чуть глубже.


Модель акторов и ее достоинства


Пара слов о модели акторов


В модели акторов прикладная работа выполняется посредством специальных вычислительных сущностей, называемых акторами, с использованием трех основных принципов:

Читать дальше →
Total votes 9: ↑9 and ↓0+9
Comments38

Имитируем управление устройствами с помощью акторов

Reading time26 min
Views4.4K

Корни SObjectizer берут свое начало в теме автоматизированных систем управления технологическими процессами (АСУТП). Но использовали мы SObjectizer в далеких от АСУТП областях. Поэтому иногда возникает ностальгия из категории «эх, давно не брал в руки шашек...» Однажды из-за этого в составе SObjectizer появился один из самых объемных примеров — machine_control. Уж очень тогда захотелось «тряхнуть стариной», смоделировать задачку управления оборудованием на современном SObjectizer-е. Ну и под шумок запихнуть в пример разные вкусные фичи SObjectizer-а вроде фильтров доставки, шаблонных агентов и диспетчера с поддержкой приоритетов. Сегодня попробуем рассказать и показать, как это все работает.


The Engine Room - Steel Stacks, Bethlehem, PA
Photo by Mike Boening


Читать дальше →
Total votes 14: ↑12 and ↓2+10
Comments1

Подробнее об одном новшестве в свежей версии SObjectizer

Reading time10 min
Views2.6K

Когда мы начали рассказывать про свой OpenSource акторный фреймворк для C++ на Хабре, мы пообещали описывать некоторые особенности деталей реализации SObjectizer-а. Одна из новых фич, которая была реализована в недавно вышедшей версии 5.5.19, отлично подходит для такого рассказа. Кроме того, она интересна еще и тем, что нам пришлось взглянуть на сценарии использования SObjectizer с совершенно другой стороны. Можно даже сказать, что один из наших шаблонов оказался разорванным.


Речь идет о возможности SObjectizer-а выполнять все свои действия на одной единственной рабочей нити. Начиная с версии 5.5.19 использовать Actor- и Publish/Subscribe модели можно даже в однопоточном приложении. Понятное дело, что акторы должны будут работать в режиме кооперативной многозадачности, но в каких-то случаях именно это и требуется.


А где может потребоваться использовать акторов в однопоточном приложении?

Читать дальше →
Total votes 12: ↑11 and ↓1+10
Comments0

Подводные камни для самодельной распределенности «из коробки» в С++ном акторном фреймворке

Reading time9 min
Views6.7K

В комментариях к последней статье про шишки, которые нам довелось набить за 15 лет использования акторов в C++, вновь всплыла тема отсутствия в SObjectizer-5 распределенности «из коробки». Мы уже отвечали на эти вопросы множество раз, но очевидно, что этого недостаточно.


В SObjectizer-5 нет распределенности потому, что в SObjectizer-4 поддержка распределенности была, но по мере того, как расширялся спектр решаемых на SObjectizer задач и росли нагрузки на SObjectizer-приложения, нам пришлось выучить несколько уроков:


  • Под каждый тип задачи желательно иметь свой специализированный протокол. Потому что обмен большим количеством мелких сообщений, потеря части которых не страшна, сильно отличается от обмена большими бинарными файлами;
  • Реализация back-pressure для асинхронных агентов — это сама по себе непростая штука. А когда сюда еще и примешивается общение по сети, ситуация становится гораздо хуже;
  • Сегодня какие-то куски распределенного приложения обязательно будут написаны на других языках программирования, а не на C++. Поэтому требуется интероперабильность и наш собственный протокол, заточенный под C++ и SObjectizer, мешает разработке распределенных приложений.

Далее в статье попробуем раскрыть тему подробнее.

Читать дальше →
Total votes 12: ↑12 and ↓0+12
Comments23

Шишки, набитые за 15 лет использования акторов в C++. Часть II

Reading time11 min
Views9.3K

Завершаем рассказ, начатый в первой части. Сегодня рассмотрим еще несколько граблей, на которые довелось наступить за годы использования SObjectizer-а в повседневной работе.


Продолжаем перечислять грабли


Народ хочет синхронности...


Акторы в Модели Акторов и агенты у нас в SObjectizer общаются посредством асинхронных сообщений. И в этом кроется одна из причин привлекательности Модели Акторов для некоторых типов задач. Казалось бы, асинхронность — это один из краеугольных камней, один из бонусов, поэтому пользуйся себе на здоровье и получай удовольствие.


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


Выглядит в коде это вот так:


Читать дальше →
Total votes 16: ↑16 and ↓0+16
Comments23

Шишки, набитые за 15 лет использования акторов в C++. Часть I

Reading time10 min
Views13K

Данная статья является первой частью текстовой версии одноименного доклада с февральской конференции C++ CoreHard Winter 2017. Так уж получилось, что вот уже 15 лет я отвечаю за разработку фреймворка SObjectizer. Это один из тех немногих все еще живых и все еще развивающихся OpenSource фреймворков для C++, которые позволяют использовать Модель Акторов. Соответственно, за это время неоднократно доводилось попробовать Модель Акторов в деле, в результате чего накопился некоторый опыт. В основном это был положительный опыт, но есть и некоторые неочевидные моменты, про которые было бы хорошо узнать заранее. О том, на какие грабли довелось наступить, какие шишки были набиты, как можно упростить себе жизнь и как это сказалось на развитии SObjectizer-а и пойдет речь далее.


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

Читать дальше →
Total votes 28: ↑28 and ↓0+28
Comments70

Модель Акторов и C++: что, зачем и как?

Reading time20 min
Views38K

Данная статья является доработанной текстовой версией одноименного доклада с конференции C++ CoreHard Autumn 2016, которая проходила в Минске в октябре прошлого года. Желание сделать эту статью возникло под впечатлением о том, что в мире C++ разработчики как бы делятся на два больших и не пересекающихся лагеря. В первом лагере находятся матерые спецы, которые все видели, все знают и все умеют, за плечами у которых десятки собственноручно написанных реализаций Модели Акторов, внутрях у которых хитрые, конечно же самостоятельно сделанные, lock-free очереди и state-of-the-art механизмы обслуживания сообщений. Такие проффи сами часами могут рассказывать про тонкости многопоточного программирования (только почему-то редко это делают). Во втором лагере — зеленые новички, которых волею судьбы занесло в мир C++, которые пока слабо представляют себе различия между unique_ptr и shared_ptr, про шаблоны только слышали, а в области многопоточности имеют поверхностное впечатление только о std::thread, std::mutex и, может быть, std::condition_variable. Для людей из первого лагеря я вряд ли что-нибудь интересное расскажу, а вот разработчикам из второго лагеря попробую вкратце рассказать о том, что Модель Акторов в C++ — это нормально. И что есть ряд готовых инструментов, на примере которых можно увидеть, что же это такое.

Читать дальше →
Total votes 24: ↑24 and ↓0+24
Comments29

Нежная дружба агентов и исключений в SObjectizer

Reading time9 min
Views4.2K
Рано или поздно в программе что-нибудь идет не так. Не открылся файл, не создалась рабочая нить, не выделилась память… И с этим нужно как-то жить. В небольшом однопоточном приложении довольно просто: можно прервать всю работу и рестартовать. Это один из факторов, благодаря которому Erlang снискал себе заслуженную популярность, ведь идеология fail fast является одним из краеугольных камней Erlang-а с его легковесными процессами. Если же приложение большое, сложное и многопоточное, то не разумно рестартовать все приложение, если лишь одна из его нитей столкнулась с проблемами. Еще хуже в ситуации с реализациями Модели Акторов, в которых сотни тысяч акторов могут работать на десятках рабочих нитей. Проблема одного актора вряд ли должна сказываться на всех остальных акторах.

В данной статье мы расскажем, как мы подошли к обработке ошибок в своем фреймворке SObjectizer.

Исключениям – да, кодам возврата – нет!


Когда SObjectizer-4 появился в 2002-ом году, мы сделали большую ошибку – предпочли использовать коды возврата исключениям. И весь последующий опыт разработки на SObjectizer-4 снова и снова убеждал в одной простой истине: если ошибка может быть прогнорирована разработчиком, то она будет им проигнорирована. Поэтому при создании SObjectizer-5 мы решили использовать исключения для информирования об ошибках.
Читать дальше →
Total votes 11: ↑9 and ↓2+7
Comments4

SObjectizer: проблема перегрузки агентов и средства борьбы с ней

Reading time7 min
Views3.3K

В предыдущих статьях мы несколько раз упоминали о такой проблеме, как перегрузка агентов. Что это такое? Чем это грозит? Как с этим бороться? Обо всем этом мы и поговорим сегодня.


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


Проблема усугубляется еще и тем, что взаимодействие посредством асинхронных сообщений и использование подхода fire-and-forget прямо таки провоцирует возникновение перегрузок (fire-and-forget – это когда агент A получает входящее сообщение M1, выполняет его обработку и отсылает исходящее сообщение M2 агенту B не заботясь о последствиях).

Читать дальше →
Total votes 16: ↑16 and ↓0+16
Comments6

Пример использования policy-based design в С++ вместо копипасты и создания ООП-шых иерархий

Reading time9 min
Views16K
Язык C++ очень часто обвиняют в неоправданной сложности. Конечно же, язык C++ сложен. И с каждым новым стандартом становится все сложнее. Парадокс, однако, состоит в том, что постоянно усложняясь, C++ последовательно и поступательно упрощает жизнь разработчикам. В том числе и обычным программистам, которые пишут код попроще, чем разработчики Boost-а или Folly. Чтобы не быть голословным, попробую показать это на небольшом примере «из недавнего»: как в результате адаптации к различным условиям тривиальный класс превратился в легкий хардкор с использованием policy-based design.
Много примеров кода
Total votes 18: ↑17 and ↓1+16
Comments6

Information

Rating
1,258-th
Location
Гомель, Гомельская обл., Беларусь
Registered
Activity