Сервіс подій в SQL-сервері

Гліб Уфімцев

Занурення в проблематику

Досить нерідко у розробників клієнт-серверних додатків виникає необхідність організувати
якийсь механізм, що дозволяє по події на SQL-сервері повідомити того або іншого клієнта. Ще частіше це
є рожево-блакитною мрією замовника, щоб розробник реалізував такий механізм. Наприклад, при
перевищенні лімітів відвантаження якому-небудь споживачеві, повинні бути негайно повідомлені менеджери,
працюють з цим споживачем. Деякі замовники систем вимагають (а мріють про це всі замовники без
виключення), щоб при зміні якихось даних, у решти користувачів системи ця інформація
автоматично оновлювалася, причому негайно. Тут не обговорюватиметься доцільність такого
вимоги (воно має багато підстав для критики), тут обговорюватимуться тільки шляхи вирішення.
Microsoft SQL-сервер має штатний засіб для організацій повідомлень – alerts, але цей засіб має
вельми обмежене застосування, за великим рахунком не дає можливість створити на його основі
гарантовано працюючий механізм. І ось чому: Зв'язок з клієнтською програмою може бути
здійснена шляхом посилки e-mail або емуляцією посилки "net send". І те, і інше незручне для отримання
повідомлення.

Засіб e-mail незручно з причин:

a) немає гарантії доставки, пошта може втрачатися.
b) пошта може "застрягти" на проміжних вузлах.
c) потрібна обов'язково наявність протоколу TCP / IP
d) потрібна наявність smtp-сервера і настройка поштового профілю.
e) потрібна особлива настройка SQL-сервера, щоб він зміг посилати листи.
f) потрібна наявність у кожного клієнта, що чекає події, поштової скриньки.
g) у клієнтській програмі потрібно організувати поштовий клієнт.

Здійснення шляхом "net send" незручна з наступних причин:

a) немає гарантії доставки, оскільки це організовано через засіб mailslot, що не має такої гарантії.
b) потрібна наявність коректного дозволу імен NETBIOS в мережі.
c) потрібна наявність на клієнтові "Клієнт для мереж Мікрософтвер".
d) задіяний стандартний mailslot, це може мати перетин з іншими програмами.

І в цілому засіб alerts незручно необхідністю реєстрації кожного клієнта як оператор і
відповідної налаштуванням. Тобто для найпростіших випадків alerts застосувати можна. Але для більшості
випадків воно непридатне.

Відомі реалізації і концепції

Широкій громадськості відомі кілька варіантів реалізації механізму повідомлення сервером
клієнта. Це:

1. Створення об'єкту (Extended Stored Procedure або ActiveX), за допомогою якого SQL-сервер повідомляє
клієнта через сокети TCP / IP. При цьому на клієнтові організована прослушка, тобто клієнтська програма стала
сервером TCP / IP.
Недоліки цього методу:
a) Прив'язка до протоколу TCP / IP. У мережі, де використовується тільки IPX, NETBEUI або AppleTalk, такий механізм
не застосувати.
b) Немає асинхронності. Якщо ця подія генерується з трігера, будуть проблеми продуктивності.

2. Створення об'єкту (Extended Stored Procedure або ActiveX), за допомогою якого SQL-сервер повідомляє
клієнта через named pipes або mailslots. При цьому на клієнтові організована прослушка того або іншого.
Недоліки цього методу:
a) потрібна наявність коректного дозволу імен NETBIOS в мережі.
b) потрібна наявність на клієнтові "Клієнт для мереж Мікрософтвер".
c) у разі використання mailslot немає гарантії доставки.
d) у разі використання named pipes, це не можна застосувати на клієнтських комп'ютерах з операційною
системою Windows 95/98/Me, так як named pipe можна створити тільки в операційній системі на архітектурі NT.
e) Немає асинхронності. Якщо ця подія генерується з трігера, будуть проблеми продуктивності.

3. Періодичний опитування SQL-сервера клієнтом (періодичне читання спеціальної таблички евентов).
Це дуже простий шлях, але, тим не менш, вільний від більшості вищеперелічених недоліків.
На жаль, цей метод має свої специфічні 2 недоліки: a) отримання повідомлення може бути
затримано на величину таймауту опитування та b) при маленькому таймаут виникає істотний трафік.
Тим не менш, при невеликому кол-ве сесій, цей метод цілком придатний і незаслужено обійдений увагою.

Пропонований варіант рішення

Вашій увазі пропонується варіант вирішення проблеми, вільний від вищеперелічених (всіх
перерахованих вище!) проблем, але разом з тим досить простий. Ідея така: на сервер поміщається
якийсь двійковий об'єкт, який sql-сервер може викликати (а це може бути тільки Extended Stored
Procedure або ActiveX-об'єкт), що має два невзаімосвязанние методу.
Перший метод створює за допомогою функції Win32API CreateEvent об'єкт ядра Win32, іменований "event" з
унікальним найменуванням, переданим параметром. Далі викликається функція Win32API WaitForSingleObject,
наткнувшись на яку, потік зупиняється і стоїть в очікуванні, поки цей об'єкт ядра не засигналить.
Звертаю вашу увагу, на той факт, що таких об'єктів ядра може бути створено скільки завгодно.
Це обмежено тільки кол-вом хендлов в системі.
Другий метод викликає об'єкт ядра event по імені, заданим параметром, за допомогою функції
Win32API SetEvent і виставляє йому властивість "signaled". Як тільки це відбудеться, потік з першим методом
пробуджується і повертає управління процесу, що викликав. Другий метод, не чекає результату, а
повертає управління своєму процесу, що викликав відразу ж після виставляння властивості "signaled". Таким
чином досягається асинхронність.
Тепер залишається тільки зробити процедури, що зберігаються T-SQL,
керівники цим об'єктом і потрібна функціональність "у нас в кишені". Клієнтська програма в
окремому потоці запускає збережену процедуру очікування події, передаючи параметром унікальний
ознака-адресу. Це може бути і ім'я користувача, і ім'я комп'ютера, і будь-який рядок. Головне, щоб це
була унікальний ідентифікатор в межах клієнт-серверної системи. Процедура, що зберігається поверне
результат лише в разі, якщо для цього адресата згенерує подія. При отриманні події,
процедура перезапускається. При закритті програми потік очікування події просто прибивається через
TerminateThread.
На перший погляд ця методу володіє "жахливим" недоліком – існує постійний
коннект з sql-сервером, який більшу частину часу нічого не робить. Але це тільки перше враження.
Насправді, задіюються ресурси тут тільки на підтримку конекту – це щось декілька кілобайт
на сесію. І все! Більше ніяких відчутних ресурсів не витрачатися, особливо на тлі переваг, які
описані нижче. Про додаткові ліцензії можна теж не турбуватися, якщо вибрана модель
ліцензування "Per server". У цьому випадку з однієї машини може бути скільки завгодно коннектов до
sql-серверу, це все одно займатиме рівно одну клієнтську ліцензію.

Готове рішення

Рішення складається з ActiveX-об'єкта у вигляді файлу AlgoEvt.dll і двох збережених процедур spWaitForEvent і
spRaiseEvent. Перед використанням цей файл треба помістити на сервер і зареєструвати ActiveX-об'єкт з
допомогою системної утиліти regsvr32.exe. Далі вся робота проводитиметься через процедури, що зберігаються.
У готовому рішенні реалізована трохи більша функціональність, ніж в описаній концепції. Крім
самого факту події, можна передати також довільну інформацію у вигляді рядка в розмірі до 250
символів. Кожна процедура має два параметри. Перша – це унікальний ідентифікатор-адреса, про який
говорилося вище, а другий параметр – додаткова передається інформація. spWaitForEvent треба викликати
з клієнта з окремого потоку (пріоритет потоку можна вибрати найнижчий). При отриманні події,
процедуру треба перезапустити. Тайм-аут виконання запиту треба задати нескінченний.

Рішення тут: SQLEventsService.zip

Переваги рішення

1. Незалежність від мережевого протоколу і настройок мережі. Був би коннект до sql-серверу.
2. Асинхронність. Ініціатор події не чекає, коли клієнт отримає подію. Можна ініціювати події
в тригері, повернення відбувається вмить.
3. Відсутність затримки доставки.
4. Гарантія доставки.
5. Відсутність практичних обмежень на к-ть клієнтів, які очікують події.
6. Не витрачаються ресурси, за винятком незначних ресурсів на підтримку з'єднань.
7. Відсутність "лівого" мережевого трафіку.
8. Не потрібно додаткових сервісів і програм.
9. Не потрібно додаткових налаштувань sql-сервера.

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*