Pull to refresh

Comments 23

По поводу SEDA, исхожу только из вашего описания — реализовать задачу как последовательность событий/шагов, каждый из которых выполняется асинхронно можно без проблем и не используя акторы. Через композицию функций где каждая выполняется одна за другой и асинхронно(т.е. передается на выполнение в пул потоков). Можно через пулы сообщений/данных, у вас есть n сущностей или групп сущностей каждая отвечает за некоторый функционал, и есть пулы сообщений(или просто данных на вход), вы помещаете ваши данные в первый пул, скажем организованный просто как FIFO(т.е. очередь) а сущности-обработчики просто выбирают эти данные обрабатывают их и после помещают в пул сообщений следующей сущности-обработчика или группы сущностей-обработчиков.
Но вы не рассказали самое главное это хранение состояния в акторе, и создание актора на каждую сущность в системе — пользователя, обработчика событий(может быть много акторов) и т.д., хотя я тут не совсем корректен вы об этом мельком сказали и тут же написали что так делать не нужно.
Почему то негативно отозвались о прозрачном распределенном взаимодействии которое должна предоставлять система акторов, просто на основании того что вам нужны специализированные протоколы передачи данных(что я вижу в общем то проблемой конкретно данной реализации, ничего не мешает[кроме трудозатрат] определить общий интерфейс и для каждой группы акторов настраивать[предварительно написать] специализированный протокол межсетевого взаимодействия.
Ни чего не сказали о подходе fault-tolerance, хотя это скорее специфика конкретной реализации а не модели акторов, хотя это и вытекает из модели акторов.

По поводу SEDA, исхожу только из вашего описания — реализовать задачу как последовательность событий/шагов, каждый из которых выполняется асинхронно можно без проблем и не используя акторы.
Вы правы, можно. Подход SEDA был упомянут для того, чтобы обозначить альтернативу варианту, когда акторы создаются на каждый чих. При этом SEDA-подход (если мы не привязываемся к оригинальному фреймворку SEDA) не говорит о том, как именно реализованы стадии. Это могут быть просто рабочие нити (или короутины), связанные каналами (скажем, в языке Go это естественным образом ложиться на Go-шные каналы и goroutines). Могут быть и акторы, как в нашем случае. Может быть еще что-то. Имхо, с акторами все-таки удобнее, чем с голыми нитями.
Но вы не рассказали самое главное это хранение состояния в акторе, и создание актора на каждую сущность в системе — пользователя, обработчика событий(может быть много акторов) и т.д., хотя я тут не совсем корректен вы об этом мельком сказали и тут же написали что так делать не нужно.
Про то, что акторы удобны для хранения изолированных состояний я пытаюсь говорить на протяжении уже ряда статей. Другое дело, что не всегда выгодно оформлять состояние отдельной сущности именно в виде актора. Тот же самый пользователь, который залогинился в систему, может быть представлен отдельным актором. А может быть всего одной из записей в одном единственном акторе, который следит за всеми залогиненными пользователями.

В зависимости от задачи можно предпочесть один подход другому. Моя цель была показать, что если не рассматривать альтернативы и тупо использовать возможности фреймворка по созданию большого количества акторов, то можно столкнуться, как минимум, с двумя неприятными проблемами. Можно и не столкнуться. Но знать об этом заранее нужно. Мы вот, например, изначально об этом не знали, набили шишек. Теперь относимся к выбору между этими вариантами очень серьезно.
Почему то негативно отозвались о прозрачном распределенном взаимодействии которое должна предоставлять система акторов
Не совсем так. Речь шла о том, почему мы со временем отказались от поддержки таких возможностей «из коробки». И одна из основных причин была как раз в том, что для устранения перечисленных в статье проблем со встроенной распределенностью требуется очень приличное количество трудозатрат.
Ни чего не сказали о подходе fault-tolerance, хотя это скорее специфика конкретной реализации а не модели акторов, хотя это и вытекает из модели акторов.
Эта тема затрагивалась в предыдущей статье (раздел «Коды ошибок vs Исключения»). Если этого недостаточно, то скажите, каких именно подробностей вам не хватило, постараюсь раскрыть тему.
По поводу удобства акторов в чистом виде или использования синхронных вызовов.
Как показала практика, действительно нужна гетерогенная модель, где есть два механизма. Наличие только асинхронного подхода имеет свои плюсы, а так же минус в виде больших трудозатрат на некоторые задачи бизнес логики, которые могли бы быть решены проще и быстрее с наличием синхронных вызовов, и им не нужна та стабильность кода, которая должна быть обеспечена разработчиком асинхронного актора.
В то же время в системе с синхронными вызовами, когда дело доходит до какого-нибудь RPC, то асинхронность появляется, как правило, сама собой, так как, например, при взаимодействии по тому же TCP/IP нужно послать сообщение по сети и получить ответ, и желательно это делать асинхронно, чтоб обеспечить в целом большую производительность приложения.

По поводу распределенности.
Возможно, не стоило от нее отказываться, а как альтернативу оставить некоторую именно Вашу оригинальную реализацию протокола взаимодействия акторов, и дать дополнительно обобщенный интерфейс, реализуя который каждый, кому нужен иной протокол, мог бы расширить возможность вашего фреймворка с учетом своего протокола. Для реализации такой гибкости за счет пользователя фреймворка потребовались бы дополнительные трудозатраты. На самом деле такие трудозатраты оказываются не так велики. Можно дать возможность пользователю набирать некоторую цепочку из обработчиков пакета и каэждое из звеньев которой можно реализовать самому.
Например, разработчик нуждается в передачи json данных с указанием границы пакета и чтоб эти данные были сжаты с помощью gzip. Можно предложить, некоторый интерфейс в фреймворке, который бы отвечал за прием и отправку сообщений (publish/subscribe), а реализовывать его мог бы и сам разработчик если его не устраивает стандантная реалзиоация. Полученные реализации можно было бы объединить в цепочку прохождения данных. Например это могло бы выглядеть так:
Chain<FrameReader, GZipDecompressor, JsonDeserializer, MyDataHandler, JsonSerializer, GZipCompressor, FrameWriter>, гле каждый из классов, переданных в класс-цепочку Chain релазиовывал бы пару методов на прием и отправку сообщений, а сам класс цепочку Chain так же был бы одной из реализаций такого интерфейса. Это дало бы возможность построения протоколов любой сложности за счет сил пользователя фреймворка.
С сетевыми протоколами можно было бы пойти аналогичным путем, определив общий интерйфейс абстрактного канала данных, а реализация могла бы бчть поверх TCP/UDP или еще какого-то протокола, а так же можно дать возмоность работать через другие механизмы IPC в рамках одной машины (очереди, sharedmemory и т.д) для запуска несколькоих компонент в рамках одной машины. Так же реализация канала может быть и в рамках одного процесса без какого либо IPC. Все это дало бы большую гибкость: пользватель фреймворка реализовал акторы в рамкаж одного процесса, но потом некоторую часть решил вынести в другой и это бы ему обошлось сменой транспотра между компонентами и небольшая реорганизация кода для сборки двух разных компонент

Ну и добавлю от себя: если вам захотелось взять и сделать с нуля свой собственный акторный фреймворк, который бы защищал разработчика от обсуждавщихся выше граблей, то это не самая хорошая идея. Занятие абсолютно неблагодарное. Да и вряд ли окупаемое. Это уже проверено. На людях.

Часто задаюсь вопросом: почему люди, что-то сделавшие, другим советуют этого не делать? Вполне возможно, у другого человека/команды ход развития событий будет иной…
Спасибо за столь развернутый комментарий. По поводу синхронности-асинхронности соглашусь. По поводу распределенности «из коробки» нужно будет подготовить более обстоятельный ответ. Поэтому пока выскажусь вот по этому поводу:
Часто задаюсь вопросом: почему люди, что-то сделавшие, другим советуют этого не делать? Вполне возможно, у другого человека/команды ход развития событий будет иной…
Дело в том, что когда ты что-то делаешь сам, ты лучше понимаешь, насколько мало приходится заниматься тем, чем хочется и как много времени и сил отнимает рутина. При разработке своего фреймворка/блиблиотеки хорошо, если хотя бы 20% времени уходит именно на разработку. Гораздо больше тратиться на отладку, баг-фиксинг, тестирование, подготовку примеров, документации, вывод всего этого дела во внешний мир, на публику. Тут, на Хабре, недавно было интересное интервью. Там хорошо рассказано о том, во что реально обходится вывод чего-то в OpenSource.

Даже если инструмент не выводится на публику, а остается внутренним инструментом компании, в которой он был разработан, то ситуация принципиально не облегчается. Может быть даже, напротив, становится хуже. Ведь внутренний продукт вряд ли будет настолько же хорошо задокументирован (хотя бы задокументирован). Следовательно, значительную часть времени разработчик инструмента будет тратить на обучение тех, кто инструментом вынужден пользоваться. А если документация будет как обычно для внутренних разработок, т.е. почти никак, то и сам разработчик с годами будет тратить все больше и больше времени на то, чтобы разобраться в своем же старом коде. Значительная часть времени будет уходить на поиск проблем с фреймворком. Причем даже не самого фреймворка, сколько проблем в коде тех, кто его использует. А использовать правильно сложно, т.к. документация оставляет желать… Да и сам фреймворк, будучи внутренним продуктом, не сможет одинаково хорошо закрывать все хотелки даже внутренних пользователей. Поэтому его иногда будут использовать неправильно, это будет приводить к проблемам, разбирательство с которыми будет отнимать время и силы.

Кроме того, не нужно думать, что можно создать фреймворк, который будет нравиться всем. Не думаю, что это в принципе возможно, ведь фреймворк — это не 100-долларавая купюра :) Так что среди внешней публики обязательно найдется некоторое количество активных горлопанов критиков, которые будут объяснять и вам, и окружающим, что то, что вы сделали — это говно, оно не работает, оно хуже, чем что-то другое, оно вообще в принципе не нужно, поскольку такие задачки нормальные программисты сами решают на коленке за 15 минут.

В общем, кроме собственно вынашивания идей для фреймворка и их реализации, есть еще куча говна всего, чем приходится заниматься. Пока сам в эту кучу не вляпаешься, то и не подозреваешь, насколько она огромная и вонючая ;)
По поводу распределенности «из коробки». Начал было писать комментарий. Но он настолько быстро стал расти в размерах, что уже начал напоминать отдельную статью. Видимо, именно статьей я его и сделаю. А здесь дам ссылку. Думаю, что обсуждать вопросы, связанные с IPC будет проще в комментариях к отдельной статье.
Крутая у вас серия статей, спасибо за огромный труд!
Вам спасибо!
Есть планы продолжать серию. Так что если есть желание узнать что-то конкретное или нужно что-то прояснить, или рассказать подробнее, то обозначьте что именно. И нам будет проще при подготовке следующих статей. И вам, читателям, будет интереснее.
И так из «акторов» получаются «микросервисы».
В общем-то да, на акторах строить микросервисы достаточно просто. Но, имхо, актор все-таки более мелкая единица, чем «микросервис». Так, «микросервис» может быть реализован посредством нескольких кооперативно взаимодействующих акторов.
Если сервис действительно микро — то понятия получаются одного уровня дробности. Но из сервисов собирать более комплексные сервисы абсолютно нормально, так что при таком подходе акторы микросервисы и есть, а сервис собранный из них — просто уже не совсем микро.
Не понятно почему. Микросервис — это то, что видно внешнему миру (т.е. другим микросервисам). Между тем, далеко не каждый актор может быть виден снаружи. Т.е. запросто может быть микросервис из кооперации трех акторов, в которой один актор известен внешнему миру, а два других актора видны только этому актору.
Внутренний сервис — не сервис? Ок.
Возможно, это мое личное заблуждение, но как по мне, разговор о микросервисах имеет смысл только когда у микросервиса есть доступный снаружи интерфейс (и соответствующий публичный контракт для его использования). Тогда можно строить сеть взаимодействующих микросервисов. Если же что-то скрыто от внешнего мира, тогда это уже не микросервис.

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

А актор, для меня, — это поток, который самостоятелен, инициативен — сам берёт себе работу, и так или иначе доводит её до конца.

А вот если актор «берёт себе работу» из «очереди заданий», и работа у него достаточно однообразна — понятие сервиса и актора пересекаются до неразличимости.
Лично для меня сервис — это поток, занимающийся одним видом деятельности, по относительно однотипным запросам на эту деятельность, приходящим к нему откуда-то со стороны.
Такое ощущение, что мы скатываемся в спор о терминах. Как по мне, микросервис — это не поток. Это именно что сервис со своим внешним интерфейсом. У него внутри может быть как один поток исполнения, так и несколько.
Ну, я с терминов собственно и начал)
И почему «скатываемся»? Ничего плохого в том, чтоб разобраться в терминах, я лично не вижу.

Сервис для Вас — обязательно процесс? Ок.
Ну, я с терминов собственно и начал)
Ну как бы нет:
И так из «акторов» получаются «микросервисы».

Поинт в том, что микросервис может быть актором. Тогда вы правы. Но так же микросервис может быть представлен группой акторов.
Сервис для Вас — обязательно процесс? Ок.
Сервис для меня — это специфицированный и доступный снаружи интерфейс.
В кавычках. Термины.
Задача комментария была — показать между ними параллель. Упомянуть смежное понятие, про которое тоже уже понаписано много букв про особенности, плюсы и минусы, грабли и т.п.
Ну так я еще раз повторюсь: параллель прослеживается, когда мы оказываемся в ситуации, когда актор достаточно большой, чтобы играть роль сервиса. Но у акторов, в принципе, гранулярность может быть и меньше. Т.е. сервис может быть образован из группы акторов. Но не наоборот.

Т.о. ваша параллель работает не всегда.

Но, опять же повторюсь, на акторах строить микросервисы достаточно просто.
Забавно, что ровно этот же мессадж Вы написали ещё раз, habrahabr.ru/post/335304, и собрали там куда больше активности )
Активность вокруг статей про SObjectizer и опыт его использования вообще вещь какая-то случайная :)
Sign up to leave a comment.

Articles