Мониторинг терминальной сети на PostgreSQL
Ведь самая минимальная версия MS SQL, которую Билл Гейтс продает с функционалом SQL JOB стоит три тысячи долларов (и то такая цена возможна со множеством ухищрений). А сравнимая с PostgreSQL версия SQL-сервера у Билла Гейтса стоит $54,990.00 per processor. Для терминальной сети - это совершенно невозможные суммы. Кто может заплатить за небольшую терминальную сеть из 200-х термналов Биллу Гейтсу более 10 миллионов долларов? Терминалы еще и воруют иногда. Одно дело - украли ящик, стоимостью 2-3 тысячи долларов. Другое дело - украли ключи к лицензионному MS SQL за 50 тысяч долларов! Как только к вопросу подключается Билл Гейтс - крашеный железный ящик с кампутером за $200 становится дороже навороченного джипа. Абсурдная и невозможная ситуация!
Мониторинг терминальной сети предназначен для оценки состояния каждого терминала, сколько купюр каждого достоинства в купюроприемниках, сколько и когда забрала инкасация, если есть автоматы выдачи сдачи - то сколько в хоперах осталось купюр, сколько загрузил туда кассир, каково состояние принтера, насколько хватит бумаги, жив ли вообще терминал - не отвалился ли он от сети и так далее. Все это терминал должен сообщить о себе серверу терминальной сети.
В описанном ниже решении опущено множество практических сложностей, но зато хорошо виден принцип работы. В данном примере у меня сервер терминальной сети выполнен на Linux, а мой терминал для этого примера работает под Windows. Хотя технология MONO и PostgreSQL мне позволяют свободно выбирать платформу запуска как терминала, так и сервера - хоть платный билогейтсовский Windows, хоть стабильный, бесплатный и с открытым исходным кодом Linux.
Итак все начинается того, что у меня есть некая вьюшка, которая аккумулирует сведения о состоянии терминала, а на сервере есть некая табличка для сбора сведений о состоянии терминалов:
Работа мониторинга основана на функции dblink (аналог билогейтсовской функции OpenRowset). На этой функции делаем одну-единственную процедуру:
1: CREATE OR REPLACE FUNCTION "TerminalMonitoring"(character varying)
2: RETURNS integer AS
3: $BODY$
4: DECLARE
5: ConnectionString ALIAS FOR $1;
6: "_TerminalID" character varying(250);
7: _now character varying(250);
8: "_ИмпортПрименяемости_i" integer;
9: "_ИмпортПрименяемости_ДатаИмпорта" character varying(250);
10: "_ИмпортСкладскихОстатков_i" integer;
11: "_ИмпортСкладскихОстатков_ДатаИмпо" character varying(250);
12: "_ИмпортТоваров_i" integer;
13: "_ИмпортТоваров_ДатаИмпорта" character varying(250);
14: "_ЗаказыДисков_i" integer;
15: "_ЗаказыШин_i" integer;
16: str1 character varying(4000);
17: BEGIN
18:
19: Select
20: "TerminalID"::character varying,
21: now::character varying,
22: "ИмпортПрименяемости_i",
23: "ИмпортПрименяемости_ДатаИмпорта"::character varying,
24: "ИмпортСкладскихОстатков_i",
25: "ИмпортСкладскихОстатков_ДатаИмпо"::character varying,
26: "ИмпортТоваров_i",
27: "ИмпортТоваров_ДатаИмпорта"::character varying,
28: isnull("ЗаказыДисков_i",0),
29: isnull("ЗаказыШин_i",0)
30: into
31: "_TerminalID",
32: _now,
33: "_ИмпортПрименяемости_i",
34: "_ИмпортПрименяемости_ДатаИмпорта",
35: "_ИмпортСкладскихОстатков_i",
36: "_ИмпортСкладскихОстатков_ДатаИмпо",
37: "_ИмпортТоваров_i",
38: "_ИмпортТоваров_ДатаИмпорта",
39: "_ЗаказыДисков_i",
40: "_ЗаказыШин_i"
41: from "CurrentState";
42:
43: str1:=
44: 'insert into "TerminalMonitor"
45: ("TerminalID",
46: "Data",
47: "ИмпортПрименяемости_i",
48: "ИмпортПрименяемости_ДатаИмпорта",
49: "ИмпортСкладскихОстатков_i",
50: "ИмпортСкладскихОстатков_ДатаИмпо",
51: "ИмпортТоваров_i",
52: "ИмпортТоваров_ДатаИмпорта",
53: "ЗаказыДисков_i",
54: "ЗаказыШин_i")
55: VALUES ('
56: || '''' || "_TerminalID"::character varying || ''','
57: || '''' || _now || ''','
58: || "_ИмпортПрименяемости_i"::character varying || ','
59: || '''' || "_ИмпортПрименяемости_ДатаИмпорта" || ''','
60: || "_ИмпортСкладскихОстатков_i"::character varying || ','
61: || '''' || "_ИмпортСкладскихОстатков_ДатаИмпо" || ''','
62: || "_ИмпортТоваров_i"::character varying || ','
63: || '''' || "_ИмпортТоваров_ДатаИмпорта" || ''','
64: || "_ЗаказыДисков_i"::character varying || ','
65: || "_ЗаказыШин_i"::character varying
66: || ');';
67:
68: if (str1 is NULL) then
69:
70: --RAISE EXCEPTION 'string is null';
71: return 2;
72:
73: else
74:
75: Begin
76: perform dblink_exec(ConnectionString, str1,true);
77: return 0;
78: EXCEPTION
79: when others then
80: --журнал целлесообразно вести только при отладке
81: insert into debug1(txt) values (SQLSTATE || ' : ' || SQLERRM) ;
82: return 4;
83: END;
84:
85: end if;
86:
87: EXCEPTION
88: when others then
89: return 1;
90: END;
91: $BODY$
92: LANGUAGE 'plpgsql' VOLATILE
93: COST 100;
94: ALTER FUNCTION "TerminalMonitoring"(character varying) OWNER TO postgres;
Которую следует вызывать вот так:
select "TerminalMonitoring"('host=10.10.10.3 dbname=Disk user=postgres password=ffffff');
где 10.10.10.3 - это адрес терминального сервера, логин и пароль доступа к нему. Использованная здесь функция Isnull, изначально отсутсвующая в ПГ - выглядит вот так.
Теперь все что нам осталось - несколько щелчков мышкой для построения задания - и терминальный мониторинг готов:
Все что нам нужно для того, чтобы этот функционал работал на терминалах - это в один клик установить Pg_Agent и проверить чтобы ревис PG_AGENT работал. После чего мы можем видеть результат мониторинга как на сервере, так и в журналах терминала:
Обратите внимание, что размер пакета совсем небольшой и в основном состоит из моих адских имен переменных. Один пакет проверки состояния терминала сьедает 1КБ трафика, поэтому какие-то ухищрения по уплотнению тут наверное будут излишними. при часовом интервале проверки - это 12КБ в сутки, за несколько месяцев набежит мегабайт стоимостью в несколько рублей. Защита трафика в этой технологии осуществляется средствами внешними к PostgreSQL - либо VPN терминальной сети, либо критические поля можно зашифровать средствами криптографии PostgreSQL.
|