LINUX.ORG.RU

Как спроектировать логику взаимодействия различных компонентов программы?

 ,


0

1

Привет, эксперты.

Пишу сейчас демона, в задачи которого входит общение с различными устройствами по сети и запись некоторых данных/событий в БД. Написал пару классов устройств. При запуске программы, демон подключается к БД, создает несколько объектов-устройств, которые подключаются к назначенным IP-адресам и начинают получать данные с устройств путем их опроса по таймерам.

Я не уверен, как лучше сделать взаимодействие м/у кодом демона и объектами-устройствами в плане записи всякого в БД.

Пока в голову пришли следующие варианты.

Вариант 1.

Передавать ссылку на объект-подключение_к_БД каждому созданному объекту-устройству и пусть он через эту ссылку пишет в БД все, что ему надо. Минус данного подхода: объекту-устройству нужно знать про класс-подключение_к_БД и уметь с ним работать (соотв., если я сейчас заменю либу mysqlpp на что-то другое, надо будет переделывать классы объектов-устройств). И еще непонятно, насколько это будет потокобезопасно.

Вариант 2.

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

Вариант 3.

Сделать некую очередь сообщений, подлежащих записи в БД, на каждом объекте. Опрашивать объекты-устройства на предмет того «есть ч0 положить в БД?» из кода демона по таймеру и, если что-то есть, то демон это записывает в БД. Из минусов, непонятно, как это скажется на адекватности работы программы, если устройств будет не 10, а, например 100 или 1000 (хотя навряд ли их будет больше 10-20 в реальности при любых обстоятельствах). Ну, типа, время опроса всех железок увеличивается пропорционально их количеству.

Собственно, как пишут подобные вещи нормальные пацаны? Можно не давать конкретный ответ, а указать книгу/статью, которую можно прочесть для просвещения в данном вопросе.

Не совсем то, что я говнокодил, но я заводил объект для работы с базой (в моем случае один, глобальный, на всю программу), а дальше дергал его из разных мест, правда программа была однопоточной. А для того, чтобы обеспечить себе замену базы данных тебе придется лишь переписать класс этого объекта для работы с базой. Так что делай его в виде виртуального класса, от которого будешь наследоваться в зависимости от СУБД, которую будешь использовать. Но это годится если ты не сильно привязан к базе и не делаешь чего-то специфичного.

peregrine ★★★★★ ()
Последнее исправление: peregrine (всего исправлений: 1)

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

Вариант 1 реализуем в форме перередачи не объекта подключения к БД, а обёртки над ним, таким образом детали того что там за подключение скрываются. А теперь внимание: это то же самое что вариант 2.

И да, API к БД скорее всего не потокобезопасен, поэтому обращение к нему нужно либо синхронизировать (в любом случае добавит задержки, в случае большого количества событий взорвётся), либо добавить (единую) очередь куда воркеры кладут события, а мастер уже выгребает оттуда и пачкой отправляет в БД. Разумеется, ни по каким не таймерам, а по сигналу на condition variable.

slovazap ★★★★★ ()
Ответ на: комментарий от slovazap

Не знаю что за каша у тебя в голове про «иерархичность»,

Банальное отсутствие знаний.

но так всегда и делают - есть API для централизованных вещей, пораждающиеся воркеры только его и дёргают.
либо добавить (единую) очередь куда воркеры кладут события, а мастер уже выгребает оттуда

Где про эти подходы можно поподробней почитать?

реализуем в форме перередачи не объекта подключения к БД, а обёртки над ним, таким образом детали того что там за подключение скрываются.

Т.е. класс-обертка, внутри него объект-подключением_к_БД и, например, мьютекс, чтобы это самое подключение защищать от гонки?

s3rjke ()

На базе чего софт? В Qt я просто дергаю сигналы-слоты, и оно прекрасненько между потоками встает в очередь и пишет в БД в одном месте. Я конечно не эксперт, но вроде работает. Работает достаточно надежно, чтобы я не задавался вопросами о переделке.

I-Love-Microsoft ★★★★★ ()
Последнее исправление: I-Love-Microsoft (всего исправлений: 1)
Ответ на: комментарий от s3rjke

Где про эти подходы можно поподробней почитать?

Понятия не имею. По мне так это самоочевидно.

Т.е. класс-обертка, внутри него объект-подключением_к_БД и, например, мьютекс, чтобы это самое подключение защищать от гонки?

Да. Не меняя кода воркеров туда можно запихнуть что угодно - и подключение с мьютексом, и отдельное подключение на каждого воркера, и очередь, и чёрта лысого.

slovazap ★★★★★ ()
Ответ на: комментарий от I-Love-Microsoft

На базе чего софт? В Qt я просто дергаю сигналы-слоты, и оно прекрасненько между потоками встает в очередь и пишет в БД в одном месте. Я конечно не эксперт, но вроде работает. Работает достаточно надежно, чтобы я не задавался вопросами о переделке.

C++. Ну, вроде как, на сервак без DE тащить Qt не очень красивым кажется. :-)

А у вас прямо демон написан с использованием Qt, без всякого GUI?

s3rjke ()
Ответ на: комментарий от slovazap

Да. Не меняя кода воркеров туда можно запихнуть что угодно - и подключение с мьютексом, и отдельное подключение на каждого воркера, и очередь, и чёрта лысого.

А что понимается под воркерами в рамках моей задачи? Это объекты-устройства или что-то другое?

s3rjke ()

Да простят меня посетители LOR-а, но этот топик как раз яркий пример того, зачем мы сделали свой велосипед. Как раз для того, чтобы такие задачи без плясок с бубном могли решать неопытные разработчики, у которых не хватает пока опыта проектирования и реализации подобных вещей. А опытные разработчики чтобы тратили меньше времени.

ТС-у: все три варианта имеют право на жизнь, а выбор между ними будет зависеть от различных дополнительных факторов. Как то:

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

* степень важности снятой с устройства информации и тяжесть последствий от ее потери. Например, если прочитанная из устройства информация не может быть просто так получена повторным опросом (например, устройство после опроса сбрасывает текущие показатели в ноль), то желательно минимизировать время, в течении которого полученная информация храниться в ОП. Грубо говоря, прочитали и сразу записали, после чего можем опрашивать другое устройство. Либо же потеря незафиксированных данных не страшна и в случае рестарта демона можно просто перечитать текущие показания с устройств. В этом случае появляется возможность опросить все устройства, затем одним махом все данные закинуть в БД;

* что за БД (локальная с низкой латентностью и с возможностью более-менее достоверно узнать результат операции сохранения в БД) или удаленная (латентность может быть выше, возможны разрывы связи или эпизодические случаи резкого увеличения времени сохранения данных в БД).

В зависимости от ответов на эти (и другие попутные вопросы) зависит и вариант реализации.

Но вообще в таких задачах различные виды message-passing-а рулят и бибикают. Здесь может найти применение любое из:

* модели publish-subscribe: данные с устройств публикуются в топиках, объект для записи в БД подписывается на соответствующие (все) топики, автоматически получает опубликованные данные и пишет их в БД;

* модели CSP (т.е. каналы как в Go): данные с устройств пишутся в канал(ы), объект для записи БД читает из канала и фиксирует данные в БД;

* модель Акторов: акторы для опроса устройств, актор для записи в БД. Данные после опроса отсылаются сообщением актору для записи в БД.

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

Но при небольшом количестве устройств, быстром опросе и быстрой записи в БД, вообще ни о чем таком не нужно заморачиваться. Простое однопоточное приложение, в котором в одном цикле все делается.

eao197 ★★★★★ ()
Ответ на: комментарий от eao197

раздает коннекты по запросу акторам,

какойто изврат, почему бы не аллоцировать новый «актор коннекта»

Deleted ()
Ответ на: комментарий от Deleted

Спасибо, одну даже недорого купить можно свежий экземпляр. :-)

s3rjke ()
Ответ на: комментарий от eao197

Спасибо за развернутый ответ и упоминание ключевых слов, по которым можно гуглить. Читая пост понял, что я про часть требований еще даже не в курсе, придется уточнять.

Но при небольшом количестве устройств, быстром опросе и быстрой записи в БД, вообще ни о чем таком не нужно заморачиваться. Простое однопоточное приложение, в котором в одном цикле все делается.

Устройств навряд ли будет больше 20, БД, скорее всего, будет на этом же сервере локально валяться. Хочется все-таки сделать «как правильно», а не «как просто», в том числе и ради своего профессионального развития.

s3rjke ()
Ответ на: комментарий от Deleted

какойто изврат, почему бы не аллоцировать новый «актор коннекта»

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

eao197 ★★★★★ ()
Ответ на: комментарий от s3rjke

Хочется все-таки сделать «как правильно»

Правильно — это когда:

a) все работает. Надежно и устойчиво;

b) сделано в срок и в бюджет;

c) в коде легко разобраться и количество грязных хаков сведено к минимуму.

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

eao197 ★★★★★ ()
Ответ на: комментарий от eao197

Но когда кто-то следит за коннектами, проще ограничивать их число

вообще если нельяз ограничить кол-во акторов определенного типа то все плохо в дизайне 8)

Deleted ()

Вариант 4.

Передавай каждому объекту ссылку на **очередь**. Объект туда пишет данные в некотором промежуточном формате. По окончании опроса всех объектов, эта же очередь пишется в базу уже как надо.

  • объект ничего не знает про особенности реализации базы
  • база не обязана иметь возможность параллельного доступа
  • ответственность за блокировки переносится с базы на объект очереди.
anonymous ()
Ответ на: комментарий от slovazap

«Понятия не имею. По мне так это самоочевидно.»

это самоочевидно вообще-то паттерном прокси зовётся

https://ru.wikipedia.org/wiki/Proxy_(шаблон_проектирования)

только ТС делает не додумал его до конца: для общения с БД нужен отдельный класс (или, может, даже просто отдельные функции, но не методы), отчего у него и нарушается эта его иерархичность

next_time ★★★★★ ()
Ответ на: комментарий от s3rjke

Где про эти подходы можно поподробней почитать?

В. Эммерих «Конструирование распределённых объектов»; Эндю Таненбаум, М. ван Стеен «Распределенные системы. Принципы и парадигмы»; Мартин Фауллер «Архитектура корпоративных приложений».

iZEN ★★★★★ ()
Ответ на: комментарий от next_time

Изобретение механизма общения, судя по всему, отменяется: нужно писать кроссплатформенную службу/демона, чтобы оно и под оффтопиком завелось.

Поэтому думаю применить Qt ради сигналалов-слотов для межклассового взаимодействия (плюс Qt'шные сетевые классы вместо голых сокетов), а для «демонизации» процесса плюсовую либу Poco, там вроде есть подходящий класс ServerApplication.

s3rjke ()
Ответ на: комментарий от iZEN

Спасибо за рекомендации. Архитектуру корпоративных приложений советовали выше, начал читать. Правда, в начале автор выделяет корпоративные приложения в эдакий особый класс приложений (большие объемы данных, которые еще и долго хранятся, _возможное_ большое кол-во параллельно работающих пользователей, _возможная_ горизонтальная масштабируемость и т.п., что явно оверкилл для моей задачки) и в плане более общего материала рекомендует читать Frank Buschmann «Pattern-Oriented Software Architecture», которую я на русском не нашел. :-(

s3rjke ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.