Перехоплювачі повідомлень в sobjectizer

Ідея про те, що в SObjectizer бажано було б мати якийсь механізм перехоплення повідомлень, існує досить давно. Її реалізації перешкоджало два моменти: по-перше, не часто виникали завдання, в яких подібний перехоплення був би критично важливий (оскільки такого механізму в SObjectizer досі немає, то можна сказати, що таких завдань не було) [1]; по-друге не хотілося ускладнювати SObjectizer додатковими механізмами.

Проте, часом з'являються завдання, які могли б бути вирішені за допомогою перехоплення повідомлень простіше, ніж без перехоплення. Як приклад такого завдання можна розглянути агента, обслуговуючого серверний TCP / IP сокет. Цей агент повинен обробляти не один сокет, а кілька - сам серверний сокет, на якому виконуються операції accept, і по одному сокету на кожного підключився клієнта.

Подібний серверний агент міг би для кожного клієнтського підключення створювати окремого клієнта, обслуговуючого тільки одне з'єднання. Серверний агент створював би дочірніх агентів таємно. тобто про їх існування не потрібно нікому знати, зовні має бути видно тільки серверний агент.

Але для таких неявних агентів виникає проблема: користувач серверного агента відсилає повідомлення msg_send_package. msg_unblock_channel. msg_close_channel серверного агенту. Причому користувач може відсилати ці повідомлення доцільно. В результаті серверного агенту довелося б отримувати ці повідомлення тільки для того, щоб переслати їх потрібного дочірньому агенту. Але, наприклад, для msg_send_package це занадто марнотратне рішення.

Якщо говорити більш точно, то завдання, в яких був критично важливий перехоплення і перемаршрутізація повідомлень, існували і успішно вирішувалися за допомогою бібліотеки MBAPI. Але MBAPI на додаток до перехоплення і перемаршрутізаціі повідомлень додавала ще деякі специфічні можливості. А ось критичних до перехоплення на рівні саме SObjectizer-повідомлень завдань дійсно не було.

Перехоплювачем може служити об'єкт, який реалізує наступний інтерфейс:

Метод message_interceptor_t :: handle отримує в якості аргументу source_info структуру з повним описом початкового примірника повідомлення (до якого входить ім'я агента-власника, ім'я повідомлення, ім'я агента-одержувача, покажчик на тіло повідомлення, ідентифікатор комунікаційного каналу, в який відсилається повідомлення і т. д.). Аргумент current_info містить повний опис поточного екземпляра повідомлення. Початкова та поточне опис може не збігатися при переході від одного перехоплювача в списку до іншого.

Метод message_interceptor_t :: handle повинен повернути в параметрі result ті значення, які замінюють параметри вихідного повідомлення. Так, якщо перехоплювач замінює ім'я одержувача, то це ім'я має бути задано в result.

Перехоплювач може вказати:

  • що прохід по списку перехоплювачів потрібно зупинити, а екземпляр повідомлення викинути і не виконувати диспетчеризації повідомлення;
  • що прохід по списку перехоплювачів потрібно зупинити і використовувати значення current_info і result для формування зміненого примірника повідомлення. Цей екземпляр йде на диспетчеризацію;
  • що прохід по списку перехоплювачів потрібно продовжити і використовувати значення current_info і result для формування нового current_info перед зверненням до наступного перехоплювачі.

Що перехоплювач може змінити:

  • ім'я одержувача повідомлення;
  • ідентифікатор каналу в який відіслав повідомлення.

Для установки перехоплювачів можна використовувати такі функції:

Для вилучення перехоплювача може використовуватися функція:

Примітка. Відкритим залишається питання про те, що робити при вилученні перехоплювача, для додавання якого використовувалася функція setup_interceptor_before або setup_interceptor_after - адже тоді розривається спеціально вибудувана користувачем ланцюжок.

Передбачається задіяти перехоплювачі після того, як для примірника повідомлення буде проведена перевірка коректності (тобто задіяний метод-checker) і перед приміщенням заявок в чергу диспетчера.

Це означає, що відкладені і періодичні повідомлення будуть проходити через список перехоплювачів тільки після того, як нитка таймера відправить їх на диспетчеризацію.

Оскільки перевірка коректності повідомлення виконується при розблокованому ядрі SObjectizer, то і виклик перехоплювачів повідомлень має сенс робити при розблокованому ядрі. Це означає, що перехоплювачі можуть здійснювати виклик функцій SObjectizer Run-Time, наприклад, для відсилання нових повідомлень.

Перед зверненням до перехоплювачам створюється вихідний об'єкт message_routing_info_t з початковим описом повідомлення. Це буде об'єкт для параметра source_info. З нього ж будується об'єкт для параметра current_info.

Після цього починається рух за списком перехоплювачів. Після звернення до чергового перехоплювачі на підставі result і current_info будується нове значення current_info. Якщо перехоплювач заборонив звернення до решти перехоплювачам (тобто перехопив повідомлення), то рух по списку перехоплювачів припиняється. В іншому випадку виконується завдання в наступному перехоплювача в списку.

Після завершення руху за списком перехоплювачів:

  • якщо маршрутизація повідомлення була заборонена взагалі, то екземпляр повідомлення просто викидається;
  • в іншому випадку на підставі current_info модифікується вихідний екземпляр повідомлення і саме він відправляється на маршрутизацію.

Нижче наведено приклад перехоплювача повідомлення msg_send_package для агента, обслуговуючого серверний сокет. Цей агент створює по одному дочірньому агенту на кожне клієнтське підключення. У дочірніх агентів імена формуються за принципом :. де parent - це ім'я батьківського агента, а client - це внутрішній ідентифікатор клієнта, що збігається зі значенням comm_channel_t :: client ().

Перехоплення полягає в тому, щоб призначити повідомленням ім'я дочірнього агента в якості імені агента-одержувача (тобто провести цілеспрямовану відсилання повідомлення дочірньому агенту).