А вот ещё такое интересное сравнение у меня давно назревает: SObjectizer vs Producer-Consumer Queues.
PC тоже имхо подход хороший, асинхронный и перспективный.
PC отличает от SObjectizer как я понимаю 3 основных вещи:
1. Сообщения типизированы и тип привязан к очереди
2. Очередей обычно немного и они "известны", т.к. во-первых, обычно очереди являются основой архитектуры, а во-вторых на каждую очередь обычно заводится как минимум один поток обработки
3. Очереди, их потребители и производители обычно жёстко заданы на этапе компиляции, т.е. особой динамики не наблюдается
Каждый пункт вобщем-то привносит как свои недостатки, так и приемущества. Может у тебя по этому поводу есть ещё какие-то комментарии. В частности интересен такой вопрос — в "средне-статистическом средней-сложности" приложении на SObjectizer сколько примерно бывает агентов, сколько бывает типов сообщенй, какой примерно процент агентов только принимает сообщения, но не отсылает собственные (т.е. насколько агенты т.с. активны), насколько картина подписки агентами на сообщения динамична в период выполнения (точные цифры не обязательны, просто хочется понять картину).
Здравствуйте, remark, Вы писали:
R>А вот ещё такое интересное сравнение у меня давно назревает: SObjectizer vs Producer-Consumer Queues. R>PC тоже имхо подход хороший, асинхронный и перспективный.
Интересно, но о Producer-Consumer Queues я первый раз слышу. Не подкинешь ссылочек на описания?
На твой вопрос о среднестатических показателях отвечу чуть позже.
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, eao197, Вы писали:
E>Здравствуйте, remark, Вы писали:
R>>А вот ещё такое интересное сравнение у меня давно назревает: SObjectizer vs Producer-Consumer Queues. R>>PC тоже имхо подход хороший, асинхронный и перспективный.
E>Интересно, но о Producer-Consumer Queues я первый раз слышу. Не подкинешь ссылочек на описания?
Прямо готовых хороших ссылок нет. Можно погуглить по словам "Producer Consumer Queue" — ссылок масса. Вот несколько из первых более-менее подходящих ссылок.
Здравствуйте, remark, Вы писали:
R>В частности интересен такой вопрос — в "средне-статистическом средней-сложности" приложении на SObjectizer сколько примерно бывает агентов, сколько бывает типов сообщенй, какой примерно процент агентов только принимает сообщения, но не отсылает собственные (т.е. насколько агенты т.с. активны), насколько картина подписки агентами на сообщения динамична в период выполнения (точные цифры не обязательны, просто хочется понять картину).
Сейчас отвечу на этот вопрос, а попытку сравнения с Producer-Consumer Queues сделаю позже.
Я буду говорить только про собственные наблюдения, полученные в процессе работы над ядром SObjectizer-а, несколькими его вспомогательными библиотеками и несколькими проектами, в разработке которых я принимал участие. У нас есть еще несколько команд, которые работают над своими проектами, у них может быть чуть иная статистика.
Количество агентов в приложении обычно не очень большое. Счет идет на десятки, вряд ли сотни. Но здесь все зависит от того, оформляются ли транзакции отдельными агентами или нет. Если транзакции пассивны, т.е. не ставят таймерных заявок, то количество агентов может достигать десятков тысяч (такое у нас в одной задаче бывало). Если же агенты должны реагировать на таймерные сообщения, то их количество нужно ограничивать, поскольку несколько тысяч таймерных сообщений с близким временем срабатывания ставят на колени все приложение -- сообщения генерируются быстрее, чем успевают обрабатываться.
Поэтому опытным путем было установлено, что агентов в приложении не должно быть много. Лучше иметь одного агента, который владеет списком транзакций, чем оформлять каждую транзакцию в виде отдельного агента.
Что касается активности, то практически всегда агенты отсылают чьи-то сообщения (по моим субъективным впечатлениям именно чужие сообщения отсылаются чаще) и принимают свои сообщения. Агентов, которые работали бы только на прием мало. Мне вспоминаются только агенты, которые выполняют логирование и сохранение информации о транзакциях, или формируют мониторинговые срезы (snapshot-ы).
При этом агенты часто образуют почти замкнутые группы. Т.е. несколько агентов образуют кооперацию (как правило являются активной группой и при использовании соответствующего диспетчера разделяют контекст одной рабочей нити) и отсылают/обрабатывают сообщения друг/друга. Для общения с соседними группами используется несколько сообщений (часто сообщений глобальных агентов).
Что касается количества сообщений/событий/состояний у среднестатического агента, то их так же не много, как правило 4-6. Иногда количество событий увеличивается, если у агента есть несколько состояний и сообщение в каждом из состояний обратывается по своему. Но так бывает редко.
Динамическая переподписка агентов выполняется не часто. Как правило, это делают агенты, которые играют роль наблюдателей. Как только они обнаруживают появление нового интересующего их агента они подписываются на его сообщения. Ярким примером является агент a_communicator, который контролирует сообщения всех глобальных агентов в приложении и отвечает за их трансляцию в коммуникационные каналы. Но, как правило, прикладным агентам это не нужно, они на момент регистрации в SObjectizer полностью определяются с подпиской и уже не изменяют ее во время работы.
В статье я говорил, что попытки делать на агентах вообще все заканчиваются весьма печально. Лучше в качестве агентов (коопераций агентов) выделять отдельные самостоятельные сущности, которые могут работать независимо от других сущностей приложения. А таких сущностей в итоге получается не много.
R>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, remark, Вы писали:
R>А вот ещё такое интересное сравнение у меня давно назревает: SObjectizer vs Producer-Consumer Queues. R>PC тоже имхо подход хороший, асинхронный и перспективный.
R>PC отличает от SObjectizer как я понимаю 3 основных вещи: R>1. Сообщения типизированы и тип привязан к очереди R>2. Очередей обычно немного и они "известны", т.к. во-первых, обычно очереди являются основой архитектуры, а во-вторых на каждую очередь обычно заводится как минимум один поток обработки R>3. Очереди, их потребители и производители обычно жёстко заданы на этапе компиляции, т.е. особой динамики не наблюдается
R>Каждый пункт вобщем-то привносит как свои недостатки, так и приемущества. Может у тебя по этому поводу есть ещё какие-то комментарии.
Как оказалось, под Producer/Consumer Queues понимался паттерн, который в ACE упоминается как Pipes/Filters паттерн и для поддержки которого в ACE используются ACE_Task, ACE_MessageQueue, ACE_Stream и пр.
Комментариев, на самом деле, я могу сделать не много.
Во-первых, по моему мнению, SObjectizer можно считать одним из инструментов, который позволяет строить приложение согласно Producer/Consumer схеме. В чем-то получается проще, т.к. работа с очередями и организация цикла выборки сообщений из очереди уже встроениа в SObjectizer. В чем-то сложнее. Например, при ручной работе с очередью (можно взять ACE_MessageQueue) есть возможность ограничить размер очереди и тогда Producer будет засыпать, если очередь будет переполнена, а Consumer не успевает ее разгребать. Или, скажем, на одну очередь могут быть завязаны несколько Consumer-ов, которые распараллеливают обработку сообщений из очереди (в SObjectizer такое распараллеливание можно сделать только если сообщения будут отсылаться каждому из Consumer-ов по очереди).
Во-вторых, агентов в SObjectizer-приложениях так же, как правило, не много и потоки данных между ними довольно четко определены. Так что здесь SObjectizer очень похож на подход Producer-Consumer Queues. То же самое можно адресовать и к твоему третьему пункту. Поскольку потребители/производители в обоих подходах известны на этапе компиляции. А конкретные связи между потребителем-производителем устанавливаются уже в run-time по конфигурации и здесь можно делать совершенно разные сочетания. Например, ACE System Configuration Framework позволяет создавать произвольное количество очередей и их обработчиков, аналогичные услуги предоставляет SObjectizer SysConf.
В-третьих, на мой субъективный взгляд, Producer/Consumer паттерн позволяет строить высокоэффективные, но жестко заточенные под конкретную задачу приложения. Например, модуль получения транзакции от клиента ставит заявку в очередь верификатора заявок, тот после обработки ставит заявку в очередь модулю маршрутизатора заявок, тот в очередь обработчика и т.д. Все строго, понятно и с минимальными (по сравнению с SObjectizer) затратами. Но, как только захочется каким-то образом мониторить этот процесс (например, контролировать в real-time количество заявок в очередях) или иногда вмешиваться в него (скажем динамически заменять некоторые коды валют в заявках), то жесткость схемы начинает сказываться -- приходится вмешиваться в нее. В лучшем случае (как при использовании ACE System Configuration Framework) можно будет вставлять дополнительные модули между парами Producer/Consumer. В худшем может потребоваться модифицировать отдельных Producer-ов или Consumer-ов. Подход на сообщениях SObjectizer здесь может оказаться гибче, хотя SObjectizer и привносит в приложение дополнительные накладные расходы.
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, eao197, Вы писали:
E>Здравствуйте, remark, Вы писали:
R>>А вот ещё такое интересное сравнение у меня давно назревает: SObjectizer vs Producer-Consumer Queues. R>>PC тоже имхо подход хороший, асинхронный и перспективный.
R>>PC отличает от SObjectizer как я понимаю 3 основных вещи: R>>1. Сообщения типизированы и тип привязан к очереди R>>2. Очередей обычно немного и они "известны", т.к. во-первых, обычно очереди являются основой архитектуры, а во-вторых на каждую очередь обычно заводится как минимум один поток обработки R>>3. Очереди, их потребители и производители обычно жёстко заданы на этапе компиляции, т.е. особой динамики не наблюдается
R>>Каждый пункт вобщем-то привносит как свои недостатки, так и приемущества. Может у тебя по этому поводу есть ещё какие-то комментарии.
E>Как оказалось, под Producer/Consumer Queues понимался паттерн, который в ACE упоминается как Pipes/Filters паттерн и для поддержки которого в ACE используются ACE_Task, ACE_MessageQueue, ACE_Stream и пр.
Когда уже написал сообщение вспомнил, что SObjectizer поверх ACE, поэтому можно было упомянуть ACE_MessageQueue, но стормозил
E>Комментариев, на самом деле, я могу сделать не много.
E>Во-первых, по моему мнению, SObjectizer можно считать одним из инструментов, который позволяет строить приложение согласно Producer/Consumer схеме.
Я бы так не сказал. Основные идеи (асинхронность) схожие, но подходы мне кажутся разными.
E>В чем-то получается проще, т.к. работа с очередями и организация цикла выборки сообщений из очереди уже встроениа в SObjectizer.
При использовании PC обычно уже имеются повторно используемые компоненты (на основе одного потока и на основе пула потоков), которые выбирают сообщения из очереди. Остаётся только определить функцию (или класс-реализауию интерфейса), которая будет обрабатывать сообщения и указать кол-во потоков.
Т.ч. в этом плане — одинаково:
E>В чем-то сложнее. Например, при ручной работе с очередью (можно взять ACE_MessageQueue) есть возможность ограничить размер очереди и тогда Producer будет засыпать, если очередь будет переполнена, а Consumer не успевает ее разгребать. Или, скажем, на одну очередь могут быть завязаны несколько Consumer-ов, которые распараллеливают обработку сообщений из очереди (в SObjectizer такое распараллеливание можно сделать только если сообщения будут отсылаться каждому из Consumer-ов по очереди).
Вот это, я считаю, большой плюс — возможность прозрачно подключить больше активных обработчиков.
Помимо кол-ва обрабатывающих потоков для каждой очереди в отдельности теоретически можно настраивать её максимальный размер, приоритет потоков, ограничение по скорости обработки сообщений и т.д.
E>Во-вторых, агентов в SObjectizer-приложениях так же, как правило, не много и потоки данных между ними довольно четко определены. Так что здесь SObjectizer очень похож на подход Producer-Consumer Queues. То же самое можно адресовать и к твоему третьему пункту. Поскольку потребители/производители в обоих подходах известны на этапе компиляции. А конкретные связи между потребителем-производителем устанавливаются уже в run-time по конфигурации и здесь можно делать совершенно разные сочетания. Например, ACE System Configuration Framework позволяет создавать произвольное количество очередей и их обработчиков, аналогичные услуги предоставляет SObjectizer SysConf.
E>В-третьих, на мой субъективный взгляд, Producer/Consumer паттерн позволяет строить высокоэффективные, но жестко заточенные под конкретную задачу приложения. Например, модуль получения транзакции от клиента ставит заявку в очередь верификатора заявок, тот после обработки ставит заявку в очередь модулю маршрутизатора заявок, тот в очередь обработчика и т.д. Все строго, понятно и с минимальными (по сравнению с SObjectizer) затратами.
Очереди совсем не обязательно составлены в такую "последовательную" структуру. Очереди скорее представляют некий граф, т.е. сообщения могут передаваться циклически, условно, из одной очереди сразу в несколько и.т.д.
Например, модуль получения сообщений от клиента кладёт заявки сразу в очередь сохранения в БД и в очередь обработки.
Или модуль получения сообщений от клиента кладёт в зависимости от каких-либо параметров сообщения заявку либо сразу в очередь обработки или в некую очередь дополнительного преобразования сообщения, которая уже после преобразования положет заявку в очередь обработки.
И.т.д. Т.е. гибкость добавлять любую логику обработки есть.
Что качается ACE_Stream, то там, насколько я помню, действительно немного "урезанный" вариант PC предлагается, заточенный под конкретную задачу.
E>Но, как только захочется каким-то образом мониторить этот процесс (например, контролировать в real-time количество заявок в очередях) или иногда вмешиваться в него (скажем динамически заменять некоторые коды валют в заявках), то жесткость схемы начинает сказываться -- приходится вмешиваться в нее. В лучшем случае (как при использовании ACE System Configuration Framework) можно будет вставлять дополнительные модули между парами Producer/Consumer. В худшем может потребоваться модифицировать отдельных Producer-ов или Consumer-ов. Подход на сообщениях SObjectizer здесь может оказаться гибче, хотя SObjectizer и привносит в приложение дополнительные накладные расходы.
Кол-во заявок в каждой очереди контролируется элементарно: у очереди есть size().
По поводу вмешивания в процесс работы очередей, то тут тоже можно некоторую фантазию применить. Например, у каждой очереди можно сделать возможность задать процедуру on_message_enqueue(), которая будет синхронно при добавлении заявки в очердь что-то делать — т.е. такая точка перехвата.
Если сравнивать SObjectizer с "традиционным синхронным подходом", то всё более-менее понятно. Синхронный подход проще, но имхо серьёзный сервер на нём не напишешь.
Хотелось бы понять costs & benefits SObjectizer vs PC. Какие сильные стороны у каждого подхода, какие слабые? Или может они вообще изоморфны.
Мне видится такое отличие. В SObjectizer агент не думает над тем "что надо сделать", он концентрируется на самом событии. т.е. агент просто говорит "произошло такое-то событие". При этом ему всё равно кто что сделает по этому событию и сделает ли вообще.
В PC тот, кто кладёт сообщение в очередь думает "я кладу сообщение в очередь на сохранение в БД", т.е. он думает, что надо сделать. Хотя по прежнему производитель и потребитель информации практически полностью развязаны с помощью очереди. Т.е. неизвестно кто, когда и как сохранит сообщение в БД.
С этой т.з. PC ближе к синхронному стилю программирования: вызов функции сохрать_в_БД(), мы просто заменяем на положить_в_очередь_на_сохранение_в_БД(). Поэтому видимо можно сделать вывод о том, что PC проще Агентно-ориентированного программирования, но одновременно надо понимать менее гибкий.
Однако, это просто мысли. Иметь бы возможность воочию сравнить два равноценных проекта.
Со своей стороны могу сказать, что прецедент успешно работающего маршрутизатора коротких сообщений ( ) на очередях PC имеется. Очередей меньше десятка и они представляют основные этапы обработки сообщения. Тип сообщения во всех очередях практически одинаковый. Остальное — КТ
Здравствуйте, eao197, Вы писали:
E>Здравствуйте, remark, Вы писали:
R>>В частности интересен такой вопрос — в "средне-статистическом средней-сложности" приложении на SObjectizer сколько примерно бывает агентов, сколько бывает типов сообщенй, какой примерно процент агентов только принимает сообщения, но не отсылает собственные (т.е. насколько агенты т.с. активны), насколько картина подписки агентами на сообщения динамична в период выполнения (точные цифры не обязательны, просто хочется понять картину).
E>Сейчас отвечу на этот вопрос, а попытку сравнения с Producer-Consumer Queues сделаю позже.
E>Я буду говорить только про собственные наблюдения, полученные в процессе работы над ядром SObjectizer-а, несколькими его вспомогательными библиотеками и несколькими проектами, в разработке которых я принимал участие. У нас есть еще несколько команд, которые работают над своими проектами, у них может быть чуть иная статистика.
E>Количество агентов в приложении обычно не очень большое. Счет идет на десятки, вряд ли сотни. Но здесь все зависит от того, оформляются ли транзакции отдельными агентами или нет. Если транзакции пассивны,
Кто такие транзакции?
E>т.е. не ставят таймерных заявок, то количество агентов может достигать десятков тысяч
Количество агентов или типов агентов?
E>(такое у нас в одной задаче бывало). Если же агенты должны реагировать на таймерные сообщения, то их количество нужно ограничивать, поскольку несколько тысяч таймерных сообщений с близким временем срабатывания ставят на колени все приложение -- сообщения генерируются быстрее, чем успевают обрабатываться.
E>Поэтому опытным путем было установлено, что агентов в приложении не должно быть много. Лучше иметь одного агента, который владеет списком транзакций, чем оформлять каждую транзакцию в виде отдельного агента.
E>Что касается активности, то практически всегда агенты отсылают чьи-то сообщения (по моим субъективным впечатлениям именно чужие сообщения отсылаются чаще) и принимают свои сообщения. Агентов, которые работали бы только на прием мало. Мне вспоминаются только агенты, которые выполняют логирование и сохранение информации о транзакциях, или формируют мониторинговые срезы (snapshot-ы).
А в чём смысл владения агентом типами сообщений? Разьве не должны быть просто сообщения сами по себе?
E>
Здравствуйте, remark, Вы писали:
E>>Количество агентов в приложении обычно не очень большое. Счет идет на десятки, вряд ли сотни. Но здесь все зависит от того, оформляются ли транзакции отдельными агентами или нет. Если транзакции пассивны,
R>Кто такие транзакции?
Прошу прощения, я из своей предметной области примеры привожу на автомате, забывая о том, что собеседник об этом не догадывается
Например, если приложением является шлюз коротких сообщений, то транзакция -- это одно короткое сообщение. Если приложением является платежный шлюз, то транзакцией будет одна платежная транзакция. Если почтовый сервер, то транзакцией будет один e-mail. Ну и в таком духе.
E>>т.е. не ставят таймерных заявок, то количество агентов может достигать десятков тысяч
R>Количество агентов или типов агентов?
Агентов.
Тип агента -- это полный аналог C++ класса. А ведь даже в больших системах вряд ли удастся насчитать десятки тысяч C++ типов.
R>А в чём смысл владения агентом типами сообщений? Разьве не должны быть просто сообщения сами по себе?
Позволю себе процитировать самого себя :
Владение сообщением
Каждое сообщение в SObjectizer принадлежит конкретному агенту. Этот агент называется владельцем сообщения. Каждое сообщение имеет имя, состоящее из имени агента-владельца и имени сообщения в агенте.
Понятие владения сообщением не связано напрямую с тем, получает ли агент-владелец данное сообщение или отправляет его. Владение означает только то, что сообщение существует в системе только тогда, когда в системе существует агент-владелец.
Например, агент-лампочка может иметь два сообщения включить и выключить. Эти сообщения относятся только к агенту-лампочка, и именно агент-лампочка является получателем данных сообщений. Если в системе таких агентов несколько (лампочка_1, лампочка_2, ..., лампочка_N), то как-то нужно различать сообщения, относящиеся к каждому из агентов. Достигается это тем, что каждый агент владеет своим сообщением и в системе оказываются сообщения лампочка_1.включить, лампочка_1.выключить, ..., лампочка_N.включить, лампочка_N.выключить. Т.е. агент-лампочка владеет сообщениями, которые не отсылает, а только получает.
Другим примером может являться агент-датчик-температуры. Этот агент может владеть сообщением температура_изменилась. Каждый агент-датчик-температуры только отправляет это сообщение, но не получает его.
Могут быть случаи, когда агент владеет сообщениями, которые он не отсылает и не получает. Т.е. некий агент вводит в систему свои сообщения, которые вместо него используют другие агенты. Например, может быть агент пожарная_сигнализация, который владеет сообщением пожарная_тревога. Это сообщение отсылают датчики температуры, а получают, например, агенты автоматической системы тушения.
Т.е. на уровне агентной модели понятие владения сообщением сводится к способу формирования имени конкретного сообщения -- имя сообщения формируется из имени агента и имени сообщения.
Здравствуйте, remark, Вы писали:
E>>Во-первых, по моему мнению, SObjectizer можно считать одним из инструментов, который позволяет строить приложение согласно Producer/Consumer схеме.
R>Я бы так не сказал. Основные идеи (асинхронность) схожие, но подходы мне кажутся разными.
Ну да, в деталях будут большие различия, а вот в общих чертах очень похоже.
R>При использовании PC обычно уже имеются повторно используемые компоненты (на основе одного потока и на основе пула потоков), которые выбирают сообщения из очереди. Остаётся только определить функцию (или класс-реализауию интерфейса), которая будет обрабатывать сообщения и указать кол-во потоков.
Вполне возможно. Я просто видел реализацию PC на примере ACE, там приходилось прилагать некоторые усилия, чтобы это дело поднять. Хотя, по большому счету, детали большой роли не играют и весьма субъективны. Мне может навится синтаксический оверхед SObjectizer, но не нравится таковой в ACE, а кому-то наоборот.
R>Т.ч. в этом плане — одинаково:
R>SObjectizer:
R>
+1
E>>В чем-то сложнее. Например, при ручной работе с очередью (можно взять ACE_MessageQueue) есть возможность ограничить размер очереди и тогда Producer будет засыпать, если очередь будет переполнена, а Consumer не успевает ее разгребать. Или, скажем, на одну очередь могут быть завязаны несколько Consumer-ов, которые распараллеливают обработку сообщений из очереди (в SObjectizer такое распараллеливание можно сделать только если сообщения будут отсылаться каждому из Consumer-ов по очереди).
R>Вот это, я считаю, большой плюс — возможность прозрачно подключить больше активных обработчиков. R>Помимо кол-ва обрабатывающих потоков для каждой очереди в отдельности теоретически можно настраивать её максимальный размер, приоритет потоков, ограничение по скорости обработки сообщений и т.д.
Теоритически и в SObjectizer можно такое сделать
Например, если отсылается сообщение handler.msg_do_it. А агент handler вместо обработки просто пребразует его в real_handler_1.msg_do_it,..., real_handler_N.msg_do_it. Эдакий load-balancer
А можно и механизм load-balancing-а прямо в механизм диспетчеризации SObjectizer-а встроить.
E>>В-третьих, на мой субъективный взгляд, Producer/Consumer паттерн позволяет строить высокоэффективные, но жестко заточенные под конкретную задачу приложения. Например, модуль получения транзакции от клиента ставит заявку в очередь верификатора заявок, тот после обработки ставит заявку в очередь модулю маршрутизатора заявок, тот в очередь обработчика и т.д. Все строго, понятно и с минимальными (по сравнению с SObjectizer) затратами.
R>Очереди совсем не обязательно составлены в такую "последовательную" структуру. Очереди скорее представляют некий граф, т.е. сообщения могут передаваться циклически, условно, из одной очереди сразу в несколько и.т.д. R>Например, модуль получения сообщений от клиента кладёт заявки сразу в очередь сохранения в БД и в очередь обработки.
Вот про это я и говорил. Если потребуется, чтобы модуль получения сообщений от клиента еще и помещал заявку в третью очередь (например, для модуля выявления мошенничества через статистический анализ), то придется править модуль. А в случае отсылки сообщений изменения не нужны. Модуль статистического анализа просто подписывается на сообщения от модуля приема заявок и делает свою работу совершенно прозрачно для остальных частей системы.
R>Кол-во заявок в каждой очереди контролируется элементарно: у очереди есть size(). R>По поводу вмешивания в процесс работы очередей, то тут тоже можно некоторую фантазию применить. Например, у каждой очереди можно сделать возможность задать процедуру on_message_enqueue(), которая будет синхронно при добавлении заявки в очердь что-то делать — т.е. такая точка перехвата.
Вот-вот, а если такие перехватчики нужно будет еще и каскадировать с учетом приоритета?
Не всегда это нужно, но у нас такой a-la Aspect Oriented подход используется для наращивания возможностей нашего шлюза.
R>Хотелось бы понять costs & benefits SObjectizer vs PC. Какие сильные стороны у каждого подхода, какие слабые? Или может они вообще изоморфны.
Очень похоже на то. Но, как обычно, дъявол будет в деталях реализации.
Имхо, затраты будут приблизительно сравнимы, один подход будет выигрывать в одних частностях, а второй -- в других.
R>Мне видится такое отличие. В SObjectizer агент не думает над тем "что надо сделать", он концентрируется на самом событии. т.е. агент просто говорит "произошло такое-то событие". При этом ему всё равно кто что сделает по этому событию и сделает ли вообще. R>В PC тот, кто кладёт сообщение в очередь думает "я кладу сообщение в очередь на сохранение в БД", т.е. он думает, что надо сделать. Хотя по прежнему производитель и потребитель информации практически полностью развязаны с помощью очереди. Т.е. неизвестно кто, когда и как сохранит сообщение в БД. R>С этой т.з. PC ближе к синхронному стилю программирования: вызов функции сохрать_в_БД(), мы просто заменяем на положить_в_очередь_на_сохранение_в_БД(). Поэтому видимо можно сделать вывод о том, что PC проще Агентно-ориентированного программирования, но одновременно надо понимать менее гибкий.
С этим я практически согласен.
Только хочу подчеркнуть, что PC может создавать иллюзию синхронности. Например, успешное выполнение операции положить_в_очередь_на_сохранение_в_БД() не гарантирует сохранения заявки в БД. Скажем из-за shutdown-а (планового или аварийного) системы. Так что феномен потери заявок и в PC подходе существует, как и в случае SObjectizer-а. И для построения надежного решения требуется делать такое решение, которому подобные эффекты безразличны. Что, по моим наблюдениям, положительно сказывается на общей надежности продукта. О чем я в статье и говорил.
R>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, eao197, Вы писали:
E>Что касается активности, то практически всегда агенты отсылают чьи-то сообщения (по моим субъективным впечатлениям именно чужие сообщения отсылаются чаще) и принимают свои сообщения. Агентов, которые работали бы только на прием мало. Мне вспоминаются только агенты, которые выполняют логирование и сохранение информации о транзакциях, или формируют мониторинговые срезы (snapshot-ы).
А вот ещё такой важный вопрос хотелось бы уточнить (в свете сравнения с PC).
Сколько в среднем агентов реарирует на какое-либо событие?
На какой процент событий реагирует только один агент?
Если на событие реагирует несколько агентов, то сколько среди них (этих агентов) агентов, которые выполняют бизнес-логику, и сколько агентов, которые выполняют т.н. common facilities (т.е. какие-то базовые сервисы, типа логирования событий, просмотра событий на предмет возможности их маршаллинга во вне и т.д.)?
Здравствуйте, remark, Вы писали:
R>А вот ещё такой важный вопрос хотелось бы уточнить (в свете сравнения с PC). R>Сколько в среднем агентов реарирует на какое-либо событие?
Субъективно ~1.
R>На какой процент событий реагирует только один агент?
Опять же субьективно ~100%.
Вообще интересные вопросы. Нужно будет подумать, как из SObjectizer-а подобную статистику доставать (ведь можно же подсчитывать среднее количество подписчиков для сообщения).
R>Если на событие реагирует несколько агентов, то сколько среди них (этих агентов) агентов, которые выполняют бизнес-логику, и сколько агентов, которые выполняют т.н. common facilities (т.е. какие-то базовые сервисы, типа логирования событий, просмотра событий на предмет возможности их маршаллинга во вне и т.д.)?
Вот здесь не так просто сказать. Мы уже несколько лет используем дополнительный фреймворк над SObjectizer-ом, mbapi называется (скоро он будет опубликован на SourceForge). Так в нем существует понятие mbapi-сообщения (mbapi-msg) которое перемещается между mbapi-ящиками (mbapi-mbox). И особенность в том, что одному mbapi-mbox-у может соответствовать сразу несколько агентов. Причем доставка mbapi-msg агенту осуществляется собственным сообщением этого агента. Т.е., если одно mbapi-msg обрабатывают три агента, то в системе будет три независимых SObjectizer-сообщения. При этом получается, что одно mbapi-msg обрабатывается тремя агентами, но, фактически, каждый из них обрабатывает только свое сообщение.
Так же все зависит от применения AOP приемов (вроде перехвата и перемаршрутизации сообщения). Если их нет, то картина простая -- на сообщение (как mbapi, так и на обычное, SObjectizer-овское) реагирует один агент. А вот если используется AOP, то ситуация может изменится очень сильно. Например, бывают случаи, когда на одно и то же mbapi-msg реагируют 5-6 агентов.
А вообще эти показатели напоминают среднюю температуру по больнице
Во многих случаях только агент реагирует на сообщение. Но в некоторых случаях количество агентов-обработчиков возрастает. И вот простота реализации прикладной логики в таких случаях играет важную роль.
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Вот, навеялось пара примеров, как SObjectizer позволяет легко манипулировать сообщениями. Вероятно, для обеспечения подобной легкости в Producer/Consumer очередях потребовалось бы оснащать очереди дополнительными примочками.
Первый пример. Существует такой подход к обеспечению надежности систем: fail fast, т.е. выпадать в осадок сразу же, как проявляется какая-то серьезная ошибка. А затем быстрый рестарт. Что забавно, работает весьма неплохо, почти как в анекдоте -- "давайте выйдем и зайдем, может она сама поедет?"
Так вот, добавлял недавно в одно приложение возможность автоматического рестарта, если какой-то из агентов обнаружил непоправимую ошибку и инициировал завершение приложение посредством специального сообщения msg_fatal_error (оно специально для таких случаев предназначено). Задача была не сложной, в функции main() организовать в цикле запуск SObjectizer Run-Time (т.е. прикладной части приложения). Выход из цикла по двум причинам:
* приложение завершилось корректно (т.е. был выполнен нормальный shutdown, рестарт не нужен);
* приложение завершилось некорректно, но слишком быстро (т.е. приложение даже не смогло стартовать нормально).
Если же приложение завершалось из-за сообщения msg_fatal_error, то цикл должен был продолжаться.
Как оказалось, сложность была в том, чтобы определить, завершился ли SObjectizer Run-Time из-за msg_fatal_error или нет -- в обоих случаях функция so_4::api::start возвращала код Ok. Простейшее решение, которое было сразу же сделано -- в стартовую кооперацию был добавлен агент, который просто получал сообщение msg_fatal_error и увеличивал собственный счетчик. После возврата из so_4::api::start просто проверялось, нулевой ли это счетчик.
Второй пример. На одном из компьютеров неожидано стала подглючивать third-party библиотека -- в один прекрасный момент все обращения к ней завершались ошибкой "сервис временно не доступен", а в описании находится строка "файл fff не открыт". Складывалось впечатление, что она почему-то закрывает один из своих временных файлов, а затем не может его повторно открыть. Плохо то, что агент, который с ней работает не может распознать ее как фатальную, поскольку эта ошибка может возвращаться на разные ситуации и во многих случаях она действительно временная. Поэтому агент просто печатает сообщение об ошибке в лог и все.
Но проблема сама по себе не разрешается. Требуется рестарт приложения, после которого все работает нормально (причем может работать очень долго). В общем, пока мы рабираемся с разработчиком библиотеки в причинах такого поведения на данном конкретном компьютере требуется как-то диагностировать эту ошибку и при ее появлении рестартовать приложение.
Простейшее решение -- агент, который подписывается на сообщение логгера и проверяет все сообщения об ошибках на предмет характерной подстроки. Как только это сообщение обнаруживается, приложение рестартует. Вот такой примитивный, но очень действенный workaround.
Я привел эти примеры потому, что мне кажется, для реализации подобных примеров в Producer/Consumer очередях потребовались бы очереди, которые позволяли цеплять на себя каких-нибудь observer-ов.
используется пример из кольца объектов, которые передают друг другу по кругу токены. При большом количестве объектов (десятки тысяч) будет ли удобно и дешево использовать механизм Producer/Consumer? На SObjectizer получается просто -- каждый агент знает имя следующего за ним в кольце и все.
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
... да... что-то так для меня ничего и не прояснилось... стало только запутаннее. Я имею в виду вопрос выбора между SObjectizer и PC.
Вроде как и очень похожи, но в тоже время вроде как основополагающие принципы разные...
Первое, что хочется сказать по этому поводу. SObjectizer — достаточно зрелый фреймворк, с достаточно развитыми возможностями. PC же обычно выглядит просто как пару классов в какой-то библиотеке (например, ACE). Возможно имеет смысл реализовать некий развитый фреймворк на основе PC как отдельную библиотека. С возможностью вешать приоритетные обработчики перед кладением в очередь/после доставания из очереди, причём это сделать как для конкретной очереди, так и для всех очередей сразу (что бы можно было повесить обработчик по помещение сразу для всех очередей в системе). Сделать разные типы потребителей (однопоточный/многопоточный/один поток на несколько очередей). И т.д. и т.п. Пока я честно говоря такого не встречал (кстати если есть ссылочки на нечто подобное — киньте).
А то сейчас SObjectizer vs PC выглядит как танк против индейца
На основе того, что каждое сообщение обрабатывает ~1 агент, и на основе владения сообщениями, и на основе того, что агенты обычно отсылают не свои сообщения. Могу предположить, что агент при отсылке сообщения больше думает не в терминах "что произошло", а "что теперь надо сделать". Это мне кажется концептуально неправильно. Мне кажется, что best practice в данном случае — агент отсылает в основном свои сообщения и говорит в них "что произошло". Так код будет более сопровождаемым, т.к. нет лишнего связывания агента-инициатора с обработкой сообщения.
+ тогда по крайней мере будут лучше видны различия между SObjectizer и PC А именно в PС все связаны с очередями и путём помещения в конкретную очередь говорят, "что надо сделать". А в SObjectizer агент может быть не связан ни с кем (кроме самого фреймворка), т.к. отсылает свои сообщения, и говорит "что произошло".
Только хочу подчеркнуть, что PC может создавать иллюзию синхронности. Например, успешное выполнение операции положить_в_очередь_на_сохранение_в_БД() не гарантирует сохранения заявки в БД. Скажем из-за shutdown-а (планового или аварийного) системы. Так что феномен потери заявок и в PC подходе существует, как и в случае SObjectizer-а. И для построения надежного решения требуется делать такое решение, которому подобные эффекты безразличны. Что, по моим наблюдениям, положительно сказывается на общей надежности продукта. О чем я в статье и говорил.
+1
Да, при синхронных вызовах, действительно, создаётся ложное впечатление надёжности, ибо на подсознательном уровне синхронный вызов чего-либо ассоциируется с вызовом локальной функции.
ИМО в PC нет иллюзии синхронности... по крайней мере у меня Достигается это тем, что, во-первых, помещение в очередь выглядит не как сохранить_в_БД(), а как положить_в_очередь_на_сохранение_в_БД(), во-вторых, очереди — основные эл-ты архитектуры и, соотв., кто хоть немного знаком с проектом осознаёт асинхронность.
, никаких зрелых, функциональных фреймворков на основе PC я не видел, хотя имо это было бы полезно.
E>Вероятно, для обеспечения подобной легкости в Producer/Consumer очередях потребовалось бы оснащать очереди дополнительными примочками.
Скорее всего это бы делалось вообще не на основе очередей, т.е. просто через какой-то компонент.
Т.к. опять же PC — это не фреймворк, т.е. он практически ничего не диктует проекту, он скорее выступает как вспомогательная утилита для реализации, в отличие от SObjectizer, который выступает в роли формируешего фремворка, т.е. диктует стиль проекта/реализации (как я это понял).
Это, наверное, на данный момент одно из основных отличий.
E>Первый пример. Существует такой подход к обеспечению надежности систем: fail fast, т.е. выпадать в осадок сразу же, как проявляется какая-то серьезная ошибка. А затем быстрый рестарт. Что забавно, работает весьма неплохо, почти как в анекдоте -- "давайте выйдем и зайдем, может она сама поедет?"
E>Так вот, добавлял недавно в одно приложение возможность автоматического рестарта, если какой-то из агентов обнаружил непоправимую ошибку и инициировал завершение приложение посредством специального сообщения msg_fatal_error (оно специально для таких случаев предназначено). Задача была не сложной, в функции main() организовать в цикле запуск SObjectizer Run-Time (т.е. прикладной части приложения). Выход из цикла по двум причинам: E>* приложение завершилось корректно (т.е. был выполнен нормальный shutdown, рестарт не нужен); E>* приложение завершилось некорректно, но слишком быстро (т.е. приложение даже не смогло стартовать нормально). E>Если же приложение завершалось из-за сообщения msg_fatal_error, то цикл должен был продолжаться.
E>Как оказалось, сложность была в том, чтобы определить, завершился ли SObjectizer Run-Time из-за msg_fatal_error или нет -- в обоих случаях функция so_4::api::start возвращала код Ok. Простейшее решение, которое было сразу же сделано -- в стартовую кооперацию был добавлен агент, который просто получал сообщение msg_fatal_error и увеличивал собственный счетчик. После возврата из so_4::api::start просто проверялось, нулевой ли это счетчик.
Мне всегда нравятся общие и гибкие решения в фреймворках. Можно в SObjectizer встроить такую фичу — с каждым типом сообщений ассоциирован статический счётчик, которой показывает сколько раз это сообщение было послано.
E>Второй пример. На одном из компьютеров неожидано стала подглючивать third-party библиотека -- в один прекрасный момент все обращения к ней завершались ошибкой "сервис временно не доступен", а в описании находится строка "файл fff не открыт". Складывалось впечатление, что она почему-то закрывает один из своих временных файлов, а затем не может его повторно открыть. Плохо то, что агент, который с ней работает не может распознать ее как фатальную, поскольку эта ошибка может возвращаться на разные ситуации и во многих случаях она действительно временная. Поэтому агент просто печатает сообщение об ошибке в лог и все.
E>Но проблема сама по себе не разрешается. Требуется рестарт приложения, после которого все работает нормально (причем может работать очень долго). В общем, пока мы рабираемся с разработчиком библиотеки в причинах такого поведения на данном конкретном компьютере требуется как-то диагностировать эту ошибку и при ее появлении рестартовать приложение.
E>Простейшее решение -- агент, который подписывается на сообщение логгера и проверяет все сообщения об ошибках на предмет характерной подстроки. Как только это сообщение обнаруживается, приложение рестартует. Вот такой примитивный, но очень действенный workaround.
У меня давно закрадываются подозрения, что агенты с SObjectizer связаны, причём неявно, что ещё усугубляет ситацию.
В данном случае, наврядли, кто-то из программистов потом будет рассматривать то, что агент пришет в лог, как часть его внешнего интерфейса. Мне кажется это проблема.
Или вот ещё. Сложно проследить какую-либо логику через всю программу. Т.е. наверное постоянно встают вопросы типа, а что происходит в ответ на сообщение, которое здесь посылает агент. Это ещё должно усугубляться тем, что обычно на сообщение реагирует один агент, т.е. когда я найду одного агента, который реагирует на событие, я вполне могу успокоиться, хотя вполне вероятно, что существует ещё второй агент, который реагирует на это сообщение, я его могу упустить. А в рантайме к этому ещё добавляется невозможность нормально дебажить, т.к. обработка асинхронная...
E>Я привел эти примеры потому, что мне кажется, для реализации подобных примеров в Producer/Consumer очередях потребовались бы очереди, которые позволяли цеплять на себя каких-нибудь observer-ов.
используется пример из кольца объектов, которые передают друг другу по кругу токены. При большом количестве объектов (десятки тысяч) будет ли удобно и дешево использовать механизм Producer/Consumer? На SObjectizer получается просто -- каждый агент знает имя следующего за ним в кольце и все.
Нет, но вроде ты говорил, что и в SObjectizer не стоит делать всё на агентах, что это будет дорого и неудобно.
E>
З.ы. давно хотел спросить, вся эта так сказать научная работа, создание и документирование фреймворков, активное общение на форумах, это какой-то фирмой спонсируется или вузом, или всё на чистом энтузиазме?
Мне, например, обычно лень писать столько таких длинных постов
Здравствуйте, remark, Вы писали:
R>Первое, что хочется сказать по этому поводу. SObjectizer — достаточно зрелый фреймворк, с достаточно развитыми возможностями. PC же обычно выглядит просто как пару классов в какой-то библиотеке (например, ACE). Возможно имеет смысл реализовать некий развитый фреймворк на основе PC как отдельную библиотека. С возможностью вешать приоритетные обработчики перед кладением в очередь/после доставания из очереди, причём это сделать как для конкретной очереди, так и для всех очередей сразу (что бы можно было повесить обработчик по помещение сразу для всех очередей в системе). Сделать разные типы потребителей (однопоточный/многопоточный/один поток на несколько очередей). И т.д. и т.п. Пока я честно говоря такого не встречал (кстати если есть ссылочки на нечто подобное — киньте). R>А то сейчас SObjectizer vs PC выглядит как танк против индейца
За сравнение спасибо
Старались
Хотя когда я пытался сделать сравнение SObjectizer и TIB/Rendezvous, то сравнение выглядело аналогичным образом, но не в пользу SObjectizer. Будет возможно, ознакомься с этим продуктом, в нем есть интересные возможности. Хотя он и расчитан на межпроцессовое взаимодействие.
R>На основе того, что каждое сообщение обрабатывает ~1 агент, и на основе владения сообщениями, и на основе того, что агенты обычно отсылают не свои сообщения. Могу предположить, что агент при отсылке сообщения больше думает не в терминах "что произошло", а "что теперь надо сделать". Это мне кажется концептуально неправильно. Мне кажется, что best practice в данном случае — агент отсылает в основном свои сообщения и говорит в них "что произошло". Так код будет более сопровождаемым, т.к. нет лишнего связывания агента-инициатора с обработкой сообщения. R>+ тогда по крайней мере будут лучше видны различия между SObjectizer и PC А именно в PС все связаны с очередями и путём помещения в конкретную очередь говорят, "что надо сделать". А в SObjectizer агент может быть не связан ни с кем (кроме самого фреймворка), т.к. отсылает свои сообщения, и говорит "что произошло".
Имхо, ты сделал здесь какое-то противоречивое высказывание, может быть из-за опечатки. На самом деле очень похоже на то, что отсылка сообщения -- это уведомление о возникновении чего-либо, а не команда выполнить что-нибудь. Здесь, вероятно, проявляются корни SObjectizer, проекта SCADA Objectizer, инструмента для разработки АСУТП. Там действительно, важно иметь средства информирования о происходящих событиях (например, скорость движения конвейера изменилась, температура увеличилась до..., сработал такой-то датчик и пр.).
R>ИМО в PC нет иллюзии синхронности... по крайней мере у меня Достигается это тем, что, во-первых, помещение в очередь выглядит не как сохранить_в_БД(), а как положить_в_очередь_на_сохранение_в_БД(), во-вторых, очереди — основные эл-ты архитектуры и, соотв., кто хоть немного знаком с проектом осознаёт асинхронность.
Я не столько для тебя говорил, сколько для менее искушенных читателей
R>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, remark, Вы писали:
R>Мне всегда нравятся общие и гибкие решения в фреймворках. Можно в SObjectizer встроить такую фичу — с каждым типом сообщений ассоциирован статический счётчик, которой показывает сколько раз это сообщение было послано.
Можно. Только мне почему-то сразу такое простое решение в голову не пришло
Хотя, если задумываться о его реализации, то встает вопрос о том, насколько часто такая фича будет востребована? И окупится ее существование? Пока мне кажется, что не окупится
E>>Простейшее решение -- агент, который подписывается на сообщение логгера и проверяет все сообщения об ошибках на предмет характерной подстроки. Как только это сообщение обнаруживается, приложение рестартует. Вот такой примитивный, но очень действенный workaround.
R>У меня давно закрадываются подозрения, что агенты с SObjectizer связаны, причём неявно, что ещё усугубляет ситацию. R>В данном случае, наврядли, кто-то из программистов потом будет рассматривать то, что агент пришет в лог, как часть его внешнего интерфейса. Мне кажется это проблема.
Если рассматривать такое поведение как попытку реализации общего решения какой-то проблемы, то ты совершенно прав -- это не правильно. Но речь шла о том, как можно быстро и без лишних усилий сделать workaround для преодоления какой-то частной ситуации. Это по большому счету напоминает создание маленького shell-овского скрипта для поиска потерянного исходного файла с определенной комбинацией ключевых слов внутри
R>Или вот ещё. Сложно проследить какую-либо логику через всю программу. Т.е. наверное постоянно встают вопросы типа, а что происходит в ответ на сообщение, которое здесь посылает агент. Это ещё должно усугубляться тем, что обычно на сообщение реагирует один агент, т.е. когда я найду одного агента, который реагирует на событие, я вполне могу успокоиться, хотя вполне вероятно, что существует ещё второй агент, который реагирует на это сообщение, я его могу упустить. А в рантайме к этому ещё добавляется невозможность нормально дебажить, т.к. обработка асинхронная...
Проследить логику движения одного сообщения -- это как раз не сложно. Это почти как следить за движением одной птицы в птичьей стае -- она движется по простым и четко определенным законам. А вот понять, как работает вся система целиком (движется целая стая), вот это гораздо сложенее. Аналогия в данном случае очень удачная и очень точная, я ее часто использую, когда кого-нибудь в курс дела ввожу.
А про отладку замечание верное. Хотя для меня это никогда не было проблемой, т.к. регулярно пользоваться отладчиком я перестал еще до появления SObjectizer
E>>И еще один пример: вот здесь
используется пример из кольца объектов, которые передают друг другу по кругу токены. При большом количестве объектов (десятки тысяч) будет ли удобно и дешево использовать механизм Producer/Consumer? На SObjectizer получается просто -- каждый агент знает имя следующего за ним в кольце и все.
R>Нет, но вроде ты говорил, что и в SObjectizer не стоит делать всё на агентах, что это будет дорого и неудобно.
Да, говорил. Но именно этот пример оказался очень удобным для реализации средствами SObjectizer.
R>З.ы. давно хотел спросить, вся эта так сказать научная работа, создание и документирование фреймворков, активное общение на форумах, это какой-то фирмой спонсируется или вузом, или всё на чистом энтузиазме?
А это, как бы точнее сказать, не научная работа
Это часть моей работы. Поскольку SObjectizer у нас является внутренним проектом, на его основе созданы и внедрены несколько важных систем. Поэтому развитие SObjectizer для нас было бы желательным. И вся моя популяризаторская работа -- это попытка продвижения SObjectizer в качестве OpenSource инструмента.
R>Мне, например, обычно лень писать столько таких длинных постов
По сравнению с написанием документации длинные посты -- это развлечение
SObjectizer: <микро>Агентно-ориентированное программирование на C++.