DCOM і Microsoft Transaction Server

Олексій Шуленін (Microsoft), www.citforum.ru

Введення

Ця стаття грунтується на матеріалах доповідей, зроблених автором на щорічній конференції розробників DevCon. Враховуючи підвищений інтерес, проявлений аудиторією до даної тематики, а також зростаючу популярність Microsoft Transaction Server серед розробників багаторівневих систем незважаючи на порівняльну “молодість” продукту, було б доцільно, на мій погляд, трохи більш докладно зупинитися на призначення і функціональних можливостях Microsoft Transaction Server, опускаючи, у міру можливості, спеціальні технічні питання, порушені у доповідях, так як представляється набагато більш важливим дати читачеві загальне бачення продукту і його місце в сучасній компонентної моделі побудови програмного забезпечення, ніж дублювати документацію. Користуючись нагодою, я хотів би подякувати численних учасників конференції DevCon’97, чиї питання багато в чому визначили зміст даної статті, і головного редактора видання “Технологія клієнт-сервер” В.Ю.Чістякова за цінне обговорення низки аспектів використання Microsoft Transaction Server і надану можливість розповіді про них.

1. Стратегія розподілених обчислень. Короткий екскурс в історію.

Ідея повторного використання коду не є чимось принципово новим у світі розробки програмного забезпечення. Як відомо, лінь – двигун прогресу (J). Якщо дивитися на речі з дещо філософської точки зору, то все розвиток людської цивілізації обумовлено можливістю грунтуватися на результатах праці попередніх поколінь, розширюючи і удосконалюючи цю базу, в свою чергу, для наших нащадків. Немає нічого дивного тому, що спроби впровадження подібного підходу в області програмних технологій робилися, напевно, ще на зорі розвитку програмування. (Ми свідомо абстрагуємося від тих вироджених випадків, коли умови оплати свідомо підштовхували програмістів до винаходу велосипеда і інш.) Спочатку це, ймовірно, був примітивний перенесення шматків коду, який спростила директива #include, Потім з появою процедурних мов це стали бібліотеки процедур, нарешті, по мірі того, як об’єктна парадигма оволоділа умами і сподіваннями розробників засобів розробки, їх місце в значній ступеня зайняли класи і бібліотеки класів. Власне кажучи, перенесення основних властивостей об’єктів оточуючого нас світу в ту його прекрасну частину, яка відноситься до розробки софту, – візьмемо хоча б спадкування-вже було підпорядковано ідеї переіспользуемості. Проте ні процедури, ні класи не дозволяли повною мірою скористатися перевагами спадкування від уже розроблених незалежних модулів при створенні досить складного проекту, особливо, якщо його передбачалося розтиражувати для великого числа замовників. Основною перешкодою була практична неможливість встановити нову версію бібліотеки без шкоди для додатків, написаним з урахуванням попередніх версій API. У загальному випадку це тягло за собою повторну компоновку, а то й компіляцію проекту, що змушувало постачальника програмного рішення передавати клієнтові не тільки виконувані або (в крайньому випадку) об’єктні модулі, але і вихідні тексти програм. По-перше, очевидно, що це аж ніяк не сприяло охороні інтелектуальної власності авторів, а по-друге, навіть ці жертви ставали марними, якщо клієнт раптом вирішував використовувати засоби розробки іншого виробника. Благим намірам творців З як якогось не залежного від платформи стандарту мови програмування не судилося збутися, і з часом ринок ПЗ захлеснула хвиля компіляторів С / С + +, для кожного з яких, як водиться, негайно виникли натовпи шанувальників, готових ревно відстоювати, що їх улюблений компілятор саме цієї фірми є самим компілятором в світі. На сьогодні це число помітно зменшилася, і пристрасті самі собою вляглися-мова не про це. До нещастя, С + + не є єдиним об’єктно-орієнтованою мовою, і якщо не настільки довгий час назад замовник бажав, щоб набуті у вас компоненти, написані на тому ж С + +, інтегрувалися в середу його “домашніх” розробок, скажімо, на Pascal’e, то це була далеко не тривіальна задача, де не рятували ні исходники, ні їх косметична правка. Такі приблизно були фактори, які врешті-решт привели до ідеї створення нової технології і виробленні стандартів, що дозволяють уникнути залежності від конкретного засобу розробки компонент і проводити ефективне оновлення версій без негативних наслідків для пов’язаних з ними модулів. Реалізація цієї технології, виконана корпорацією Microsoft, отримала назву моделі компонентних об’єктів (COM).

2. Модель компонентних об’єктів. Основні поняття.

Художник не малює картину по пікселям. Спочатку на полотно лягають грубі штрихи, і тільки потім настає промальовування деталей. Ідея компонентного підходу в програмуванні виникла одночасно з програмуванням, оскільки вона відповідає, якщо можна так висловитися, природної модульності аналітичного мислення, коли рішення досить об’ємною задачі починається з її розбиття на кілька підзадач, ті, в свою чергу, дробляться на ще більш дрібні і ієрархічно більш залежні фрагменти і т.д. аж до тих деталей, вирішення яких елементарно. З тієї ж причини компоненти спрощують налагодження додатка- згадайте пошук кореня методом половинного ділення. Більш того, збірка з незалежних компонент істотно збільшує масштабованість, так як в залежності від необхідної функціональності ми можемо додати в нього ті чи інші компоненти або, навпаки, вилучити так, щоб додаток не нападав, а плавно і коректно деградувало. Ми можемо фізично “розмазати” додаток по мережі, встановивши різні компоненти на різні комп’ютери, і знову підвищити тим самим масштабованість, але вже в плані продуктивності. Нарешті, компоненти універсальні, як атоми або гени: компоненти, з яких складається ваш додаток, в різних поєднаннях з іншими компонентами можуть використовуватися при побудові нових додатків, тобто саме завдяки їм судилося збутися вікової мрії людства про повторно використовуваних коду, з якої ми почали нашу розповідь. Отже, компоненти-це круто. Тепер кілька слів про те, як це все працює.

Розподілена компонентна модель об’єктів (DCOM) – це (див. [1]) набір стандартів побудови, розміщення та взаємодії компонент і реалізують їх механізмів, які дозволяють об’єктам всередині компонент здійснювати дзвінки іншим компонентам, а також приймати і обробляти запити від інших компонент незалежно від їх положення на комп’ютері або в мережі, від способів реалізації, від того, чи є вони прикладними або об’єктами операційної системи і т.д. Для цього об’єкти (D) COM “домовляються” про надання одна одній сервісів через строго певні інтерфейси, які на ідеологічному рівні можна розглядати як свого роду зобов’язання об’єкта надати заявлену функціональність за умови виклику відповідно до опублікованими їм правилами, а на побутовому – як групи семантично пов’язаних функцій, об’єднаних в абстрактні віртуальні класи. Нехай, наприклад, маємо деякий набір функцій, оформлений у вигляді



struct I1
{
virtual void f11()=0;
virtual int& f12()=0;

}

Будь клас, навмисний використовувати цю функціональність, може бути оформлений, наприклад, у вигляді спадкування:



class AnyCls : public I1 …

Однак, що робити, якщо нам було потрібно розширити набір функцій? Переписати struct I1 означає повернутися до проблем, описаним в п.1. Отже, логічно буде звести додаткові функції в новий інтерфейс:



struct I2
{
virtual void f21()=0;

}

А як бути тоді з AnyCls? Інтерфейсів може не один і не два, а множинне спадкування виручає до певної межі, після якого виникають різного роду проблеми, починаючи від слабкої читаності програми і закінчуючи втратами в продуктивності. Рішення полягало у використанні вкладених класів:



class AnyCls
{ protected: …
public:
AnyCls {…;}
class C1 : public I1
{ public:
C1() {}
virtual void f11();
virtual int& f12();

} m1;
class C2 : public I2
{ public:
C2() {}
virtual void f21();

} m2;

}
void AnyCls::C1::f11(){ … ;}

, Які, з одного боку, упаковані в батьківському класі і не видно за його межами, а з іншого, мають доступ до його елементів. Зауважимо, що за допомогою функцій CoCreateInstance або IUnknown :: QueryInterface (Див.нижче) клієнт працює тільки з покажчиками на таблиці віртуальних функцій інтерфейсів, тобто реально створивши об’єкт того ж класу AnyCls, він тим не менш не має покажчика на нього. Таким чином він ніколи не отримує прямого доступу до внутрішніх даних об’єкту, навіть якщо б вони не були protected. Моделюванню на С + + основних механізмів роботи COM присвячена, наприклад, [2], частина IV і бажаючі завжди можуть звернутися до цієї літератури, а також до [3], яка по справедливості вважається основоположною роботою в області компонентного підходу, до сьогоднішнього дня не втратила своєї актуальності.

Інтерфейси є настільки основоположним поняттям COM, що для їх опису використовується спеціальна мова-IDL (мова визначення інтерфейсів), по своїй структурі дуже схожий на С + +. У визначенні інтерфейсу описуються заголовки вхідних у нього функцій із зазначенням їхніх імен, що повертаються типів, вхідних і вихідних параметрів, а також їх типів. Це і є та частина контракту, яка буде повідомляти, що може і як треба викликати об’єкт, який взяв на озброєння даний інтерфейс. Кожен інтерфейс отримує свій 16-байтовий глобальний ідентифікатор, який присвоюється йому програмою генерації на підставі часу створення, адреси мережевої плати і пр. Після опублікування інтерфейс фіксується і подальші зміни в ньому не допускаються. IDL допускає спадкування, так що можливі похідні інтерфейси. Більш того, всі інтерфейси є похідними від одного базового інтерфейсу під назвою IUnknown. До складу IUnknown входять 3 методу. QueryInterface використовується для отримання покажчиків на інші інтерфейси об’єкта, за умови, що покажчик на початковий інтерфейс був отриманий за допомогою CoCreateInstance. Методи AddRef і Release застосовуються для підрахунку посилань, тобто, грубо кажучи, скільки клієнтів в даний момент використовують інтерфейси даного об’єкта. AddRef виконується автоматично, як тільки з боку клієнта надходить запит на покажчик інтерфейсу. Однак якщо всередині клієнтської програми відбувається породження нового посилання на інтерфейс, про яку серверний об’єкт не здогадується, виклик AddRef покладається на клієнта. Клієнт не має права зруйнувати серверний об’єкт, але по закінченні роботи з ним він зобов’язаний викликати метод Release, який зменшить на одиничку число користувачів об’єкта. Коли воно стане рівним 0, об’єкт сам себе знищить.

У тому випадку, якщо об’єкт створений як in-process сервер (dll), тобто виконується всередині клієнтського процесу, особливих проблем не виникає. Якщо ж об’єкт реалізований у вигляді out-of-process сервера, що виконується як окремий процес на тому ж або віддаленому хості, негайно з’являється питання про передачу покажчика на інтерфейси і параметрів викликів методів між процесами. Питання нетривіальний тим більше тому, що різні комп’ютери можуть використовувати різні формати представлення даних. Відповідь полягає у використанні proxy-об’єкта усередині клієнтського процесу і заглушки всередині сервера. Proxy-це звичайний COM-об’єкт, який представляє ті ж інтерфейси, що і викликається клієнтом, однак замість безпосереднього виклику методів він приймає передані клієнтом параметри і упаковує їх для передачі засобами межпроцессного або міжмашинна (RPC-remote procedure call) комунікації. Заглушка на стороні сервера приймає і розпаковує ці параметри, викликає відповідний метод серверного об’єкту і передає їх йому. Повернення результатів клієнту відбувається за тією ж схемою в зворотному напрямку. Такий процес називається маршалинга (демаршалінгом). Зазвичай всі розвинені засоби розробки COM-об’єктів надають можливість автоматичної генерації proxy і заглушки для інтерфейсу. Якщо розробника з якихось причин це не влаштовує, він може запрограмувати нестандартний маршалинга за допомогою визначеного в COM інтерфейсу IMarshal. Виникає цікавий момент: якщо код маршалинга здатний автоматично створюватися на стадії компіляції програми, то чому б не використати цю можливість для динамічної генерації маршалеров під час виконання програми? Такий підхід (він відомий як пізнє скріплення), незважаючи на додаткові витрати, істотно підвищує гнучкість програмування, бо рішення про виклики тих чи інших інтерфейсів може прийматися по ходу справи залежно від прикладної логіки. Однак для цього клієнт повинен “на льоту” вміти отримувати доступ до інформації про інтерфейси, необхідної для виконання маршалинга. Цим вимогам відповідає бібліотека типів, яка перераховує всі інтерфейси, підтримувані тим об’єктом, для якого вона створюється, та описується в термінах вже згадуваного нами вище IDL. Як і для COM-об’єктів, при установці в registry записується ID бібліотеки та її місце розташування. При виклику засобами СОМ API бібліотека типів надає інтерфейс ITypeLib, який, у свою чергу, дозволяє отримати покажчики на інтерфейс ITypeInfo для кожного інтерфейсу, перерахованого в бібліотеці, за допомогою яких видобувається інформація про параметри методів і їх типів, необхідна для динамічного маршалинга.

Зазвичай основним споживачем цієї інформації виявляється інтерфейс IDispatch (природно, похідний від IUnknown), який напевно знайомий програмістам на Visual Basic. Методи цього інтерфейсу GetTypeInfo і GetTypeInfoCount дозволяють динамічно запитувати запущений об’єкт щодо інформації про всі його інтерфейсах. Інший метод, IDispatch :: Invoke фактично являє собою оператор switch, який в залежності від переданого значення ідентифікатора (DISPID) викликає той чи інший метод діспінтерфейса. Іноді в літературі можна зустріти судження, що Visual Basic не працює з покажчиками, тому для нього потрібно було створити якийсь універсальний механізм, що вміє працювати тільки з одним інтерфейсом IDispatch. Це не зовсім коректне зауваження. По-перше, у світлі нашої розмови про маршалинга очевидно, що єдина пара proxy-заглушка для IDispatch не в змозі передбачити перетворення параметрів для самих різних методів діспінтерфейса, тому частина перетворення неявно виконує сам клієнт, упаковуючи параметри в variant. По-друге, тільки перша версія Visual Basic розпізнавала виключно діспінтерфейси. У всіх інших трапилося QueryInterface зовсім аналогічно повертає покажчик на віртуальну таблицю, що містить реалізацію запитуваної інтерфейсу. У термінах Visual Basic це відомо як об’єктна посилання (object reference). Тобто, якщо змінна оголошена як dim x As <Something>, Де <Something>№ Object, То буде використовуватися раннє зв’язування. В першу чергу компілятор (VB5) буде шукати способи прямого виклику через віртуальну таблицю (vtable binding) і тільки в разі невдачі вдасться до діспінтерфейсу. При цьому за інформацією в бібліотеці типів він постарається знайти DISPID і кешувати його, щоб заощадити на досить дорогому виклику GetIDsOfNames в період виконання. При посиланнях типу dim x As Object або Set x = y, Де створення об’єкта y відбувається під час роботи програми, компілятор, природно, позбавлений можливості зробити які-небудь припущення про природу об’єктів х і не зможе оптимізувати їх виклики. У цьому випадку, як неважко здогадатися, буде мати місце пізніше зв’язування. Пізніше зв’язування переважно характерно для Visual FoxPro 5.0, в нього також включена оптимізація для підтримки vtable binding-см. функцію sys (2333). Іноді інтерфейси описуються як двоїсті, тобто містять діспінтерфейсние представлення для віртуальних таблиць своїх методів.

Одним з яскравих проявів переваг компонентної моделі служить OLE-автоматизація, або програмованість, тісно пов’язана з подвійними і діспінтерфейсамі. OLE-автоматизація з’явилася одним з етапів розвитку COM, і практично кожен напевно з нею стикався. Мова йде про те, що при всій повноті своєї функціональності додаток буде представляти ще більшу цінність для розробників, якщо дозволить використовувати цю функціональність не тільки в інтерактивному режимі, але і з користувацьких програм. Замість того, щоб постачати додаток додатковим API для цих цілей, автор може просто оформити його як OLE Automation сервер, що приблизно означає, що додаток свідомо “засвічує” зовні деякі зі своїх методів, що використовувалися для внутрішньої реалізації заявленої функціональності. При цьому, по-перше, досягається економія програміста праці: автору не потрібно спочатку розробляти додаток, а потім писати API до нього. По-друге, користувач мислить в тих самих категоріях, що й автор, що значно полегшує освоєння програми. В третіх-і це плюс, характерний для всієї технології COM, – надана функціональність залишається на озброєнні користувача при розробці їм якихось своїх програм, ніяк не пов’язаних з даними OLE Automation сервером. Простий приклад: якщо у нас на комп’ютері встановлено Microsoft Office, то навіщо нам винаходити велосипед і писати програму перевірки орфографії, якщо ми можемо створити об’єкт Word.Application і викликати для нього метод SpellChecking:



dim x As New Word.Application
Debug.Print x.SpellChecking(“Abra Cadabra”)

Те ж саме відноситься, наприклад, до використання статистичних функцій Microsoft Excel і т.д. В якості OLE Automation серверів можуть розглядатися не тільки офісні додатки, але і самі засоби розробки- Того ж Visual Basic або Visual FoxPro-і навіть важкі серверні продукти сімейства Microsoft BackOffice, наприклад, Microsoft SQL Server, який за допомогою SQL-DMO (distributed management objects) забезпечує виконання практично всіх адміністративних функцій з клієнтського застосування (зрозуміло, за наявності відповідних прав доступу).



dim oSQLServer As New SQLOLE.SQLServer
oSQLServer.Connect “ntalexejs”, “sa”
dim newdb As New SQLOLE.Database
newdb.Name = “sqlole”
newdb.ExtendOnDevices (“oledat=5”)
newdb.TransactionLog.DedicateLogDevices (“olelog=2”)
oSQLServer.Databases.Add newdb

3. Interface’ом про table, або компонентний підхід в базах даних

Приблизно в цей же час, а може бути, трохи раніше, розробники СУБД зіткнулися з необхідністю підтримки неструктурованих типів даних: мультимедіа, просторова інформація, папки електронної пошти і т.д. З ідейної точки зору, не приймаючи в розрахунок питань технічної реалізації, рішення, здавалося б, лежить на поверхні: переписати заново або переробити ядро ​​свого сервера баз даних, зробивши його об’єктно-реляційних і включивши туди підтримку нових типів нарівні з традиційною інформацією. Виграш очевидний: ми знову отримуємо єдиний API і повний контроль над даними і їх змінами. До жаль, мінусів при такому підході виявляється більше, ніж плюсів. По-перше, потрібно перекачування інформації з вихідних місць її зберігання в базу даних, де вона буде оброблятися, перетворюватися і повертатися назад. Природно, це не кращим чином позначається на продуктивності і надійності. По-друге, виконання запитів дуже важко піддається оптимізації, так як правила їх обробки сильно залежать від типів даних. По-третє, схема носить закритий характер і тому не є привабливою в очах розробників незалежних додатків. Нарешті, не цілком зрозуміло, що робити, коли виникне потреба в підтримці нових типів даних, – знову переписувати ядро? Тому наступним етапом стало проектування універсальних серверів баз даних, де процедури обробки неструктурованої інформації володіли можливістю гнучко вбудовуватися в технологію роботи виконавчого механізму СУБД. В одному випадку такі процедури були оформлені як динамічні бібліотеки і працювали в тому ж процесі, що і ядро ​​універсального сервера, в іншому-як самостійні процеси. При цьому частина процедур відповідала за підтримку додаткових типів даних, які поряд з традиційними зберігалися в базі, а частина (У взаємодії з мережевим сервером додатків) – за їх обробку. Незабаром між прихильниками цих двох напрямків розгорілася війна не на жарт, яка вийшла за межі змагання технологій і перемістилася на придорожні рекламні щити і у відділи з найму персоналу. Перший підхід критикувався з точки зору надійності, так як чисто теоретично можна допустити, що випадкова помилка в процедурі здатна вивести з ладу весь процес, тобто ядро СУБД, тим більше, що право на написання вбудовуваних модулів було також даровано незалежним фірмам. Другий підхід у силу ізоляції процесів один від одного виглядав більш надійним, але по тій же причині піддавався критиці з точки зору продуктивності. Останнім часом достатньою популярністю користується ідея компонентного підходу, коли дані не переносяться в базу, а використовуються за місцем їх безпосереднього зберігання. Для кожного типу даних існує компонент, який ми назвемо, скажімо, провайдером OLE DB і який за своїм принципом роботи нагадує добре відомі ODBC-драйвери, з тією лише різницею, що він вміє звертатися до даних не обов’язково реляційної природи. Кожен такий компонент “знає”, як забезпечити доступ до свого типу і являє для цього необхідні (стандартні) інтерфейси. В результаті способи подання інформації виявляються прозорими для споживача, і він зможе в одному операторі select запитувати, наприклад, інформацію з різних баз даних, електронних таблиць, документів, електронної пошти, файлової системи і т.п.

Проникнення компонентних технологій в область баз даних було навіть в якійсь мірі обумовлено історично. Перенесемося буквально на кілька років назад, і ми потрапимо в епоху розквіту персональних настільних СУБД a la xBase, де користувальницький інтерфейс, засоби програмування бізнес-логіки, забезпечення цілісності схеми зберігання даних-все було укладено всередині одного продукту. За відносно недовгий час необхідність корпоративної роботи над проектом, зростання обсягів збереженої і переробляється інформації та багато іншого привели до впровадження клієнт-серверних систем, де на сервер бази даних покладалась відповідальність за безпеку зберігання і доступу до даних, підтримку їх транзакционной цілісності, забезпечення користувальницьких з’єднань і багато іншого. На клієнті, відповідно, залишилися користувальницький інтерфейс і якась частина бізнес-логіки. Взагалі, бізнес-логіка на сервері починається з моменту проектування бази даних, і тому особисто для мене цілком розумним представляється прагнення зосередити якомога більшу її частину в тригерах, збережених процедурах і т.п. Клієнт-серверні системи отримали широке визнання і поширення аж до рівня масштабних корпоративних та загальнодержавних проектів. Неймовірно розрослася і ускладнилася бізнес-логіка, вона перестала вміщатися всередині сервера бази даних. Плюс до того продовжували впливати ті самі тенденції, які зумовили перехід від персональних баз до клієнт-серверних систем: збільшення обсягів даних, транзакционной навантаження, числа одночасних користувальницьких коннектів і т.д. У підсумку на клієнті залишився інтерфейс, на сервері баз даних-його типові функції типу підтримки цілісності схеми, резервне копіювання, тиражування, а все, що відносилося до бізнес-логікою, виділилося в самостійне проміжна ланка (middlware). Не встигли вщухнути захоплення з приводу того, як класно стало жити тепер нам усім, як з’ясувалося, що нове-це добре забуте старе, за все доводиться платити, і разом з бізнес-логікою на тендітні плечі розробників середньої ланки лягає обов’язок програмування безпеки доступу, управління потоками, обробки транзакцій, забезпечення необхідної швидкодії і масштабованості і багато чого ще, з чим раніше благополучно справлявся сервер баз даних. Більш того, настільки тісне переплетення системного і прикладного програмування не тільки подовжило терміни розробки і підняло приблизно вполовину вартість такого проекту, але, що ще більш сумно, збільшило залежність результатів від конкретного проекту і зробило практично неможливим повторне використання напрацювань у цій галузі. У цьому місці залишалося б зітхнути, розвести руками і поставити крапку, але я з дитинства не люблю казок з нещасливим кінцем, тому в нашому оповіданні з’являється нова дійова особа- Microsoft Transaction Server.

4.Microsoft Transaction Server

18 травня в рамках проходив у Нью-Йорку Дня масштабованість демонструвалася робота гіпотетичної банківської системи, клієнтами якої була приблизно чверть населення земної кулі. Загальна база даних перебувала під управлінням 20-ти серверів Microsoft SQL Server 6.5 на платформі Compaq. Ще 20 комп’ютерів імітували діяльність з боку клієнтів. Диспетчеризацію клієнтської навантаження і управління транзакціями виконували 5 серверів Microsoft Transaction Server (MTS). За день система змогла обслужити мільярд (!) Транзакцій, з яких значна частка припала на частку розподілених (тобто проходять через декілька серверів баз даних).

Microsoft Transaction Server 1.0 був випущений в грудні минулого року і в традиційному розумінні є сервером підтримки роботи додатків, складових ПО проміжного шару. Він здійснює автоматичне управління процесами і потоками, має вбудовані служби безпеки для контролю викликів і використання об’єктів, забезпечує підтримку розподілених транзакцій по протоколу двофазної фіксації OLE 2PC і інтеграцію з MS DTC, надає графічний інтерфейс для реєстрації і управління компонентами (MTS Explorer), тобто фактично надає готові засоби вирішення завдань системного програмування, які, як ми зазначили вище, неминуче виникають при розробці middleware. З цього боку позитивний аспект застосування MTS полягає в тому, що при розробці компонент не потрібно програмувати вручну реакцію на різноманітні випадки в системі. Скористаємося одним з прикладів в складі MTS і розглянемо клас Account компоненти Bank. Він має метод Post для дебітованія або кредитування певного банківського рахунку. Однак, як правило, банківська операція означає дебет одного рахунки і кредит іншого. Питання: скільки додаткового програмування буде потрібно, щоб виклик двох методів у програмі на VB, VC + + і т.д. виконувався як одна транзакція? З використанням MTS рішення стає тривіальним.

Bank.MoveMoney


dim ctxObject As ObjectContext
Set ctxObject = GetObjectContext()

On Error GoTo ErrorHandler

dim objAccount As Bank.Account
Set objAccount = ctxObject.CreateInstance(“Bank.Account”)

objAccount.Post(lngSecondAccount, lngAmount)
objAccount.Post(lngPrimeAccount, 0 – lngAmount)

ctxObject.SetComplete

ErrorHandler:
ctxObject.SetAbort

Ми значно скоротили і спростили текст прикладу, залишивши в ньому ілюстрацію самих базових моментів. Перше, на що потрібно звернути увагу-це об’єкт ctxObject, який носить назву “контекст об’єкта”. Кожен об’єкт, розміщений у MTS, автоматично отримує у відповідність такий об’єкт на весь час свого життя. Контекст об’єкта управляє участю об’єкта в транзакціях під контролем MTS (див., наприклад, методи SetComplete і SetAbort в прикладі) і надає засоби для програмної перевірки безпеки. Наприклад, якщо надходить запит на переклад великої суми, ми можемо передбачити перевірку на наявність у нього відповідних прав:



If (lngAmount > 500 Or lngAmount < -500) Then
If Not ctxObject.IsCallerInRole(“Managers”) Then
Err.Raise Number:=ERROR_NUMBER, …
Description:= “Need ‘Managers’ role for amounts over $500”
End If
End If

Установка самих ролей відбувається в MTS Explorer. Посилання на контекст об’єкта можна отримати за допомогою функції MTS API GetObjectContext (). У даному прикладі ми створюємо об’єкт Account від контексту об’єкта MoveMoney, в результаті чого він буде виконуватися всередині тієї ж транзакції, що і об’єкт MoveMoney. У загальному випадку це залежить від транзакційного атрибуту компоненти, під яким вона встановлена ​​в MTS. Можливі 4 варіанти: supported-при створенні нового об’єкта його контекст успадковує клієнтську транзакцію, якщо ж клієнт виповнюється поза транзакції, то новий контекст теж не буде транзакційних; required-то ж, що і в попередньому випадку, але в разі отстутствия транзакції у клієнта MTS відкриє нову транзакцію для об’єкта; requires new-об’єкт не успадковує транзакцію клієнта, для нього завжди відкривається нова транзакція; not supported-транзакція ніколи не буде відкрита об’єкту незалежно від наявності транзакції на клієнті.

Однією з відмінних рис MTS є надзвичайно дбайливе, я б навіть сказав, трепетне ставлення до системних ресурсів. Його девізом є активізація в міру необхідності і як можна більш швидка деактивізація об’єктів. Коли MTS деактівізірует об’єкт, той не руйнується, а стає доступний для повторного використання іншими об’єктами. По секрету скажу, що MTS часто нахабніє настільки, що може деактивизировать створений вами об’єкт при живих клієнтських посиланнях. Тобто якщо ви створили об’єкт, а потім відійшли кофейку попити, не дивуйтеся, що цей сквалига вже спер ваш об’єкт, деактивізована його і загнав кому-небудь на бік. Сердитися на нього за це не варто, тому що коли ви повернетеся і скажете “ей, хлопець, жени взад мій об’єкт”, краще почекати зайвих пів-секунди, поки MTS створить або затягне (що істотно швидше) з чергового ледачого з’єднання такий же об’єкт, ніж отримати повідомлення типу “вибач, брат, не можу-пам’ять скінчилася”. MTS деактівізірует об’єкт при викликах методів закінчення транзакції SetComplete або SetAbort. Наступного разу при повторному зверненні до цього об’єкта MTS за звичаєм втрутиться в COMовскій виклик, підсуне вам той самий ваш (а може і не ваш) об’єкт і викличе у нього затребуваний вами метод. Те ж відбувається при роботі з базами даних. Так як однією з найдорожчих операцій є установка з’єднання, то MTS схильний тримати у себе пул ODBC-з’єднань, і якщо потрібне вам з’єднання вже туди потрапило і є вільним, як ви думаєте, що зробить MTS, коли ви його попросите з’єднатися з базою? Кинеться бігом відкривати нове з’єднання? Щас прям. У переважній більшості випадків ви отримаєте його з пулу. Сміх сміхом, проте дбайливе витрачання системних ресурсів є однією з необхідних умов високої масштабованості, і не володій MTS такими можливостями, він навряд чи зумів би на звичайній в общем-то конфігурації без особливих наворотів, обробити мільярд транзакцій в добу.

Іноді виникають ситуації, коли об’єкт змушений повернути управління клієнтові не будучи готовим завершити транзакцію. Нехай у нас є об’єкт, який відповідає за введення однієї бухгалтерської проводки. Нам потрібно ввести серію проводок, скажімо, за день і звірити суму з підсумком. Якщо вони збігаються, то тільки тоді всю групу проводок можна фіксувати як єдину транзакцію. Для цих цілей контекст об’єкта має метод DisableCommit, який забороняє деактивизации і скидання внутрішнього стану об’єкта. У нашому випадку його повинен викликати об’єкт введення проводки. Після вивірки підсумків об’єктом викликається метод EnableCommit з подальшою фіксацією або відкотом транзакції.

Отже всі об’єкти, що працюють в середовищі управління MTS, можуть викликати один одного. На відміну від них базовим клієнтом називається істинний клієнт в розглянутої нами в п.3 трирівневої схемою, тобто це додаток, працююче, очевидно, не під управлінням MTS, основна мета якого забезпечувати користувальницький інтерфейс і відображати запити користувача в виклики компонент. Для управління роботою об’єктів MTS базовий клієнт використовує об’єкт TransactionContext. Незважаючи на те, що схема його застосування дуже схожа на контекст об’єкта (методи CreateInstance, Complete та Abort), базовий клієнт може тільки управляти транзакцією MTS, але не брати участі в ній. Наприклад, він не може безпосередньо відкрити з’єднання з базою даних і вставити операції над нею всередину цієї транзакції. Напевно, це правильно, тому що коли незабаром бізнес-логіка пішла з клієнтської частини, то участь у транзакціях middleware неприпустимо для базового клієнта.

VB4 умів створювати тільки однопоточні компоненти. MTS забезпечує їх підтримку, хоча це самий повільний спосіб, що загрожує взаємними блокуваннями. Припустимо, об’єкти А і В послідовно виконуються на одному потоці управління. А блокує запис Х і закінчує роботу, забувши зробити SetComplete. Управління отримує об’єкт В, якому теж потрібна запис Х, тому він простоює в очікуванні, поки А її розблокує. Але для цього А повинен отримати потік управління, який наглухо зайняв В. Маємо картину під назвою “Припливли”. У VB5 можна створювати apartment-threaded компоненти, які також підтримує MTS, що вже легше. Кожному об’єкту всередині компоненти призначається окремий потік на весь час його життя. Межі апартаментів визначаються діяльністю (activity), яка, наскільки я розумію, означає “Гірлянду” об’єктів, пов’язаних послідовними викликами. Таким чином, якщо два об’єкти належать різним діяльностям, вони можуть виконуватися паралельно незалежно від того, належать вони одній або різним компонентам. Мені здається, що логічно було б ввести в наступних версіях MTS модель робочих потоків, так щоб об’єкти не прив’язувалися жорстко до якогось потоку, а могли вільно між ними перерозподілятися і при цьому не вимагався б крос-потоковий маршалинга.

Адміністративними одиницями при розміщенні компонент в MTS служать пакети. Вважається, що компоненти всередині пакета повністю довіряють один одному, тому взаємні права компонент в пакеті не перевіряються, крім того вони запускаються в одному процесі. Очевидно, що базові виклики мають identity клієнта, тому при доступі до компонентів усередині пакета перевіряється user-id. Виклики, що йдуть з пакету, вже мають identity даного пакету, тому якщо користувачі звертаються до компонентів, а компоненти, у свою чергу, – до бази даних, то має сенс згрупувати компоненти по пакетах у відповідності з правами користувачів на доступ до бази, в іншому випадку авторизацію доведеться прописувати ручками всередині компонент. Це свого роду витрати перехідного періоду до багаторівневим системам. Перебудуйте своє мислення відповідно з новим підходом: ви вже виросли з системи “клієнт-сервер”, давайте права на базу, маючи на увазі не кінцевих користувачів, а компоненти. Зрештою, користувач взагалі тепер не працює з базою- це турбота компонент. Адміністрування користувальницького доступу до компонентів здійснюється з MTS Explorer.

MTS допускає введення всередині пакетів глобальних змінних, доступ до яких дозволено для кожної компоненти. Глобальні змінні (властивості) організовані в групи, отримати або створити які можна з допомогою SharedPropertyGroupManager. Аналогічно усередині кожної групи можна отримати або створити поділюване властивість за допомогою SharedPropertyGroup. Повернемося до нашого банківського наприклад на початку пункту. Припустимо, що ми ще хочемо протоколювати кожну проводку в журналі і привласнювати їй узгоджений унікальний номер. Ось як для цього можуть використовуватися розділяються властивості:


dim spmMgr As SharedPropertyGroupManager
Set spmMgr = CreateObject(“MTxSpm.SharedPropertyGroupManager.1”)

dim spmGroup As SharedPropertyGroup
dim bResult As Boolean
Set spmGroup = spmMgr.CreatePropertyGroup(“Receipt”, LockSetGet, _
Process, bResult)

dim spmPropNextReceipt As SharedProperty
Set spmPropNextReceipt = spmGroup.CreateProperty(“Next”, bResult)

dim spmPropMaxNum As SharedProperty
Set spmPropMaxNum = spmGroup.CreateProperty(“MaxNum”, bResult)

dim objReceiptUpdate As Bank.UpdateReceipt
If spmPropNextReceipt.Value >= spmPropMaxNum.Value Then
Set objReceiptUpdate = ctxObject.CreateInstance(“Bank.UpdateReceipt”)
spmPropNextReceipt.Value = objReceiptUpdate.Update(strResult)
spmPropMaxNum.Value = spmPropNextReceipt.Value + 100
End If

‘ Get the next receipt number and update property
spmPropNextReceipt.Value = spmPropNextReceipt.Value + 1

Висновок

Microsoft Transaction Server поєднує в собі функції монітора транзакцій і брокера об’єктних запитів. Як монітор транзакцій MTS управляє транзакціями, що проходять через декілька менеджерів ресурсів, розподільниками ресурсів (ODBC-з’єднання) і загальними властивостями, процесами і потоками. Як брокер об’єктних запитів MTS управляє розподілом компонент з комп’ютерів, використанням (у тому числі повторним) екземплярів об’єктів, а також правами та безпекою об’єктних викликів. Додатки пишуться як однокористувацький, оформляються як ActiveX dll’і, реєструються в середовищі управління MTS і починають працювати в многопользовательском режимі. Програмування для MTS не вимагає інтенсивного знання COM або Win32 API. Компоненти для MTS можуть бути розроблені з використанням широкого переліку засобів розробки як від Microsoft, так і від інших фірм. MTS підтримує товстих (Win32 через DCOM) і тонких (броузер через HTTP і ASP) базових клієнтів. Незважаючи на порівняно недавній термін виходу MTS встиг зарекомендувати себе як потужне і надійне засіб побудови та диспетчеризації ПО проміжного шару, що відповідає найсучаснішим вимогам концепції розподілених обчислень.

Список літератури


  1. Д.Чеппел. Технології ActiveX і OLE. ISBN 5-7502-0029-9

  2. Д.Круглінскі. Основи Visual C + +. ISBN 5-7502-0025-6

  3. K.Brockschmidt. Inside OLE2. ISBN 1-55615-618-9

  4. Х.Кастер. Основи Windows NT і NTFS. ISBN 5-7502-0023-X


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


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

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

Ваш отзыв

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

*

*