19 февр. 2020 г.

Обмен сообщениями между базой данных и программой

    Многие современные СУБД представляют возможности для обмена сообщениями между процессами. Чаще всего это уведомления о каких-то событиях в базе данных. Например, об изменении данных или об окончании выполнения процедуры. Принцип реализации этого механизма у всех СУБД одинаковый: один процесс (приложение) подписывается на событие от базы данных и ожидает его, а второй процесс отправляет уведомление об этом событии. В Oracle это позволяют делать процедуры пакетов DBMS_ALERT и DBMS_PIPE, в PostgreSQL команды NOTIFY и LISTEN, в InterBase/Firebird команды POST_EVENT и EVENT INIT/WAIT...
    Рассмотрим отправку сообщений из базы данных PostgreSQL и их получение в программе. Как я писал выше, PostgreSQL для обмена сообщениями использует две команды: NOTIFY и LISTEN. Команда NOTIFY отправляет текстовое сообщение с указанием имени канала всем клиентским процессам (приложениям), которые до этого выполнили в текущей базе данных команду LISTEN для этого канала. Создадим тестовую таблицу:
CREATE TABLE public.test
(
  id SERIAL NOT NULL,
  name VARCHAR(100) NOT NULL,
  CONSTRAINT PK_Test PRIMARY KEY(id)
);
и правило (можно триггер), которое при вставке строки в созданную нами таблицу будет посылать в канал "test_notify" сообщение "INSERT":
CREATE OR REPLACE RULE rule_test_notify
AS ON INSERT
TO public.test
DO NOTIFY test_notify, 'INSERT';
    Для обработки событий, публикуемых сервером базы данных в программе, воспользуемся компонентой TUniAlerter из библиотеки Universal Data Access Components (UniDAC). На форму помещаем компоненты для подключения к базе данных (TUniConnection и TPostgreSQLUniProvider) и TUniAlerter:
У объекта UniAlerter обязательно необходимо заполнить свойство Events. В этом свойстве хранится перечень событий, на которые подписывается объект (наименования событий разделяются символом "точка с запятой"). Присвоим Events наименование нашего канала "test_notify". Теперь добавим обработчик OnEvent, который срабатывает, когда вызывается какое-либо из зарегистрированных событий:
procedure TForm1.UniAlerterEvent(Sender: TDAAlerter;
                           const EventName, Message: string);
begin
  mLog.Lines.Add(DateTimeToStr(Now) + ': ' + Message);
end;
В событии OnEvent нас интересуют два параметра: EventName - наименование события и Message - текст сообщения.

    Заставить объект UniAlerter принимать сообщения можно двумя способами. Первый - это установить свойство AutoRegister=TRUE. Тогда подписка на события осуществится автоматически при открытии подключения к базе данных. Второй способ для тех, кто привык контролировать все сам - воспользоваться методами Start и Stop:
procedure TForm1.btnStartStopClick(Sender: TObject);
begin
  if UniAlerter.Active
    then begin
      UniAlerter.Stop;
      mLog.Lines.Add(DateTimeToStr(Now) + ': Stop');
    end
    else begin
      UniAlerter.Start;
      mLog.Lines.Add(DateTimeToStr(Now) + ': Start');
    end;
end;
    Теперь запустим полученную программу и попробуем вставлять данные в тестовую таблицу запуская и останавливая UniAlerter нажатием на кнопку "Start/Stop":
Сообщения "INSERT" появляются только, если объект UniAlerter находится в состоянии Active=TRUE. Необходимо учитывать, что подобный механизм обмена сообщениями связан с транзакциями. Это означает, что ожидающий процесс не получит извещение о событии, пока транзакция, в рамках которой была инициировано событие, не завершится.

    Кроме получения сообщений класс TUniAlerter умеет их и отправлять. Это делается с помощью метода SendEvent(const EventName: string; const Message: string):
procedure TForm1.btnSendEventClick(Sender: TObject);
begin
  UniAlerter.SendEvent('test_notify', 'Hello PostgreSQL');
end;
Вызов этого метода отправляет в канал "test_notify" сообщение "Hello PostgreSQL" и его получат все процессы, которые подписались на этот канал.

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

Комментариев нет: