Программисту-профессионалу
Последняя версия Visual Basic существенно отличаетс от предыдущих во всех отношениях - начиная с диалекта основного языка до типов прикладных программ, которые можно создавать с его помощью. Тот факт, что Visual Basic 4.0 предназначен в первую очередь для работы в среде новых ОС - Microsoft Windows 95 и Wnidows NT - с учетом их собственных новых возможностей, делает эти изменения еще более актуальными. Главное нововведение - комплекс средств дистанционного управления (Remote Automation), RA, - содержащееся в версии Enterprise Edition, позволяет любой программе-клиенту OLE запускать OLE-сервер дистанционно, по сети.
Возможность создания "автоматизированных" серверов OLE - это одна из ключевых новых функций Visual Basic 4.0. Она позволяет программистам, использующим Visual Basic, создавать автономные OLE-объекты, которые предоставляют свойства и методы автоматизации клиентам OLE (включая другие приложения Visual Basic) так, как это делают объекты Word for Windows WordBasic или объекты Excel Worksheet.
До сих пор серверы OLE и программы-клиенты должны были размещаться на одном ПК. Но с введением средств дистанционного управления серверы OLE, функционирующие в среде Windows 95 или Windows NT, становятс доступными для программ-клиентов, работающих под управлением Windows 3.x, Windows 95 или Windows NT на сетевых рабочих станциях.
В локальной модели средств автоматизации OLE команды от клиента проходят через модуль-инициализатор (proxy), комплектующий и передающий сообщение в модуль-переходник, где оно разбирается (распаковываетс и декодируется) и откуда посылается команда на сервер, как показано на рис.1. Механизм OLE автоматически обеспечивает наличие модулей инициализатора и переходника. Обработанные данные из сервера проходят через переходник и инициализатор на обратном пути к клиенту.
Рис.1. В локальной модели OLE команды проходят из модуля-инициализатора в модуль-переходник.
________ _______________ * ____________ ________
| OLE |-->| модуль- |------>| модуль- |-->| OLE |
| клиент | | инициализатор | * | переходник | | сервер |
|________|<--|_______________|<------|____________|<--|________|
*
В автоматизированном варианте инициализатор и
переходник стали совершенно другими, теперь в них
используются процедуры дистанционного вызова (RPC) дл
передачи запросов клиента по сети к модулю Automation
Manager (AM), функционирующему на сервере. АМ передает
данные клиента указанному OLE-серверу и ответные данные
по сети к программе-клиенту, как показано на рис.2.
Рис.2. Реализация средств дистанционного управлени требует использования модуля Automation Manager.
Машина-клиент Машина-сервер
---<------ -----<----
_____|__ _____|_____ _____ __________ ______|____ ___|____
| | | | | | | | | | | |
| OLE | | модуль- |<----------------------| модуль- | | OLE |
| клиент | | инициали- | | RPC | |Automation| | переходник| | сервер |
| | | затор | | | | Manager | | | | |
| | | |---------------------->| | | |
|________| |___________| |_____| |__________| |___________| |________|
| | | |
--->------ ----->-----
Замечательным свойством RA является его прозрачность
как для пользователя, так и для программиста. Вы можете
создать и запустить на своей машине сервер OLE, а затем
приспособить его для работы в дистанционном режиме
простым перенесением OLE-сервера на удаленный ПК, на
котором работает модуль- администратор
автоматизированного управления Automation Manager.
Затем вы регистрируете новое местоположение
OLE-сервера, используя модуль автоматизации
дистанционного управления Remote Automation Connection
Manager (в комплект Visual Basic 4.0 Enterprise Edition
входят как Automation Manager, так и Remote Automation
Connection Manager). Программисту не приходится менять
ни одной строки текста программы, чтобы внести средство
дистанционного управления в клиент- или
сервер-программу. И, поскольку регистрацию удаленного
сервера легко включить в процедуру установки
программ-клиента, пользователю не приходитс
предпринимать каких-либо дополнительных действий, чтобы
воспользоваться новыми возможностями.
Работа серверов OLE в дистанционном режиме имеет ряд существенных преимуществ, в том числе упрощенное администрирование и обслуживание распределенных прикладных систем, перенос сложных вычислительных заданий с одной рабочей станции на другую, возможность его многократного использования любой OLE программой-клиентом, многоуровневая система защиты доступа и достоверности данных. Но самое главное преимущество, по крайней мере с точки зрения Microsoft, - это способность удаленных OLE-серверов выполнять роль хранилища бизнес-правил и процедур дистанционного доступа к данным в распределенных прикладных комплектах.
Реализация зтой возможности представляет собой ключевой момент в стратегическом видении компанией Microsoft трехуровневой архитектуры деловых приложений, в которой часть пользовательского интерфейса размещена на рабочей станции клиента, бизнес-логика - на сервере отдела или сервере среднего уровня, а сверхмощные средства обработки данных - на SQL-сервере базы данных. Пользовательский интерфейс и бизнес-уровни объединены механизмом автоматизации OLE Automation, в то время как уровни бизнес-логики и баз данных - обычными связями баз данных.
Эта трехуровневая модель имеет настолько принципиальное значение в планах Microsoft, что, похоже, это единственное применение средств дистанционного управления, которое можно найти в официальной литературе по Visual Basic 4.0. Большинство примеров рассматриваемых средств на компакт-дисках, содержащих Enterprise Edition, представляют собой вариации трехуровневой модели, в которой клиенты и серверы OLE, реализованные на языке Visual Basic, в конечном счете, обеспечивают взаимодействие с удаленной базой данных. Тем не менее нетрудно увидеть, что RA содержит в себя средства, отнюдь не замыкающиеся на модели клиента и сервера баз данных.
Например, трехуровневая модель клиент/сервер не особенно облегчит задачу тому, кто просто пытаетс заставить прикладную программу, используемую в многопользовательском режиме на предприятии, работать немного быстрее черепахи. А ведь идея расчленения этой программы на отдельные функциональные блоки и с их резмещением в различных участках сети наиболее эффективным образом, а затем связывания их с помощью нового механизма открывает заманчивые перспективы! Первое, что пришло мне в голову, когда я наблюдал RA в действии, - это то, что с помощью этого механизма можно решить проблему эффективности многопользовательских баз данных в программном комплексе для рабочих групп, который я подготовил на Visual Basic 3.0.
Оказалось, что информация, содержащаяся в справочном файле демонстрационного приложения VB 4.0, подтверждает это предположение. В статье, посвященной OLE-объектам сервера, где применяются имеющиеся в Visual Basic средства доступа - Jet/DAO - к удаленной базе данных клиента и сервера, утверждается: "Чаще всего размещение сервер-объектов доступа к данным на той машине, где находится база данных, может существенно сократить загрузку сети и увеличить общую скорость передачи данных".
Я понял, что, то, что годится для удаленной SQL базы данных, годится и для "родной" базы данных Access языка Visual Basic. Для проверки своей теории я расчленил прикладную программу для рабочих групп на два компонента: программа-клиент, отвечающая за управление всеми взаимодействиями с пользователем, и программа-сервер, отвечающая за управление всеми средствами доступа к данным. Затем я воспользовалс средствами дистанционного управления и перенес программу-сервер на сервер-компьютер рабочей группы, используемый для хранения общей базы данных.
Результат превзошел все ожидания. Производительность сети практически перестала влиять на время обработки транзакций, такое расчленение привело и к значительному повышению эффективности работы приложения в целом. Более того, трафик в сети существенно уменьшился и стал более предсказуемым. Самое замечательное состоит, видимо, в том, что этот эксперимент указал очевидные пути вненсения дополнительных значительных усовершенствований в программу, что раньше было невозможно из-за ограничений, накладываемых производительностью сети.
Задача рассматриваемого типа возникла передо мной несколько лет назад, когда я занималс совершенствованием простого приложения для рабочих групп под названием Copyflow, изначально подготовленного средствами Visual Basic 2.0. Программа предназначалась для упрощения прохождения редакционных документов через сложную систему сетевых маршрутов. Если пользователь хотел переместить файл и вызывал Copyflow из меню File редактора Word for Windows или Excel for Windows, программа предоставляла ему список доступных маршрутов (наиболее вероятный адрес выбиралс заранее). После того как пользователь выбирал нужный ему адрес, программа Copyflow переносила туда файл, создавала резервную копию и затем уничтожала этот файл по старому адресу, обеспечивая таким образом в любой момент времени наличие только одного "живого" экземпляра файла в редакции. Аналогично, если пользователь желал найти или распечатать файл, Copyflow предоставляла ему списки маршрутов, к которым этот пользователь имел право доступа; затем, с помощью средств DDE вырабатывалась команда редактору Word или Excel распечатать или открыть выбранный файл.
Несмотря на некоторые шероховатости, например нечетное DDE-взаимодействие между пакетами VB и Word, программа, учитывая возможность версии Visual Basic 2.0, работала вполне удовлетворительно. Так что, когда мне нужно было добавить средства централизованного слежения за файлами в новую версию CopyFlow, новейший по тем временам Visual Basic 3.0 казался самым естественным выбором, учитывая наличие в его арсенале таких средств, как Jet Database Engine, Data Control и Data Access Objects (DAO). Особенно привлекательной оказалась чрезвычайная гибкость новых средств работы с базами данных, что позволяло составлять программу наиболее простым образом, используя стандартные средства управления данными, и затем настраивать ее работу и функции путем выборочного внедрения более сложных методов и объектов.
Я не предполагал, что разделяемая база данных будет сильно влиять на производительность программы Copyflow. Хотя с Copyflow должны были работать до 50 пользователей одновременно, предполагалось, что большинство из них будут обращаться к базе данных лишь эпизодически. В среднем каждый пользователь мог добавить к базе данных около 5-6 записей в день - при этом каждое обращение приводило бы к одной или двум записям в таблицы - и инициировало бы еще меньше запросов к базе данных. (Запрос к базе данных осуществляется лишь тогда, когда пользователь использует средства поиска файлов или извлечени информации программы Copyflow для выяснения истории конкретного файла.)
Мои первые тесты производительности, казалось, оправдали мой оптимизм. Но, когда началась эксплуатаци новой версии, и совместный банк данных стал расти, я, как и многие VB-программисты, обнаружил что даже крайне редко используемая общая база данных может сильно ухудшить производительность VB-программ. Слишком большой объем данных приходится перегонять по сети каждый раз, когда пользователь обращается к цетральной базе данных. В моем случае ситуация осложнялась частыми перегрузками сети.
К великой моей досаде я понял, что относительна редкость обращений среднего пользователя к базе данных не такое уж благо. Дело в том, что при этом система Windows практически всегда за время между обращениями к данным выгружала из памяти огромные DLL-библиотеки баз данных Visual Basic и, соответственно, должна была снова их загружать, прежде чем Copyflow могла начать перемещение данных по сети.
В такой ситуации возможность разделить программу Сopyflow так, чтобы программы доступа к базе данных размещались на сетевом сервере рядом с самими данными, казалась ниспосланной свыше. Я считал, что это позволит добиться следующих трех преимуществ по сравнению с общепринятой моделью многопользовательской базы данных, применявшейся в существовавшей версии Copyflow.
При любом расчленении программы прежде всего нужно решить, какие функции возложить на каждую из ее частей. Моя задача состояла в том, чтобы извлечь из клиентской части программы все подпрограммы доступа к данным и полностью избавить ее от необходимости загружать какие-либо DLL-библиотеки. Для этого мне пришлось исключить из текста программы не только команды обращения к механизмам запросов и обработки данных элемента управления Jet, но и любую ссылку на объекты таблиц или записей, а также использование полей Data Control и связи данных.
К счастью, Copyflow содержала лишь три подпрограммы обращения к данным: RecordMove, регистрирующая в базе данных каждое перемещение файла; SearchDB, подпрограмма поиска; и другая подпрограмма поиска, встроенная в подпрограмму Load Sequence, которая используется при выборке информации для вывода на экран истории файла. Кроме того экран выборки информации Get Info был реализован с использованием средств управления данными (Data Control) и нескольких связанных между собой средств управления текстом (Text Control) и метками (Label Control).
Проще всего было перенести на удаленный сервер подпрограмму RecordMove, постренную в виде процедуры без передаваемых параметров, а не функции. Начальна версия RecordMove с помощью DAO (объектов доступа к данным) вносила записи в две таблицы, названные Files и Moves, внутри разделяемой базы данных программы Copyflow и была не слишком сложной (лист. 1). Поскольку практически в каждом предложении первоначальной версии RecordMove использовались DAO, нужно было почти целиком перенести их на новый сервер базы данных OLE Automation. С этой целью в 32-разрядной версии Enterprise Edition Visual Basic 4.0 я разработал новый проект под названием DBSVR.VBP. И сразу же добавил к этому проекту классовый модуль, создав в нем общий класс с именем cfDBsvr. Затем я приступил к преобразованию имевшейся у меня подпрограммы RecordMove в общую подпрограмму в классовом модуле. Для этого переделал прежний текст программы с учетом изменений в версии Jet Database Engine 3.0 (заменив, например, команды Open Table на команды Open Recordset, имеющие параметр dbOpenTable) и использовав новую конструкцию With...End With для упрощения оброащений к полям в каждой таблице (см. лист. 2).
Лист. 1. В начальной версии программы Copyflow эти простые предложения применялись для внесения записей в две таблицы, File и Move совместной базы данных.Sub RecordMove (fName$, Path$, DestDir$, uName$) Dim DB As Database, DS As DynaSet, [[lcc]] T1 As Table, T2 As Table Dim sPath%,dPath% sPath%= GetOueueNum%(Path$) dPath%= GetOueueNum%(DestDir$) ' Открыть базу данных и таблицы Set DB = OpenDatabase(MyDatabase$) Set T1 = DB.OpenTable("Files") Set T2 = DB.OpenTable("Moves") ' Найти fName$ в таблице Files T1.Index = "idxFiles" T1.Seek "=".fName" T1.LockEdits = False ' Добавить новую запись в Files, если Seek (поиск) не дал результатов BeginTrans If T1.NoMatch Then 'если файла в базе нет, то T1.AddNew Else T1.Edit End If ' Всегда добавлять новую запись в Moves T2.AddNew ' Записать данные о перемещениях в таблицу Files T1("FileName") = fName$ T1("mFrom") = sPath$ T1("mTo") = dPath$ T1("Date") = Date$ T1("Time") = Time$ T1("mBy") = uName$ T1("Path") = dPath$ ' Записать данные о перемещениях в таблицу Moves T2("FileName") = fName$ T2("mTo") = dPath$ T2("mFrom") =sPath$ T2("MovedBy") = uName$ T2("mDate") = Date$ T2("mTime") = Time$ ' Закончить T1.Update T2.Update CommitTrans T1.Close T2.Close DB.Close End Sub
Лист. 2. Новая версия подпрограммы RecordMove программы Copyflow, В ней отражены изменения в версии Jet Database Engine 3.0 и использована нова конструкция With...End With для упрощения обращений к полям таблиц.Public Sub RecordMove(ByVal fName$, ByVal Path$, ByVal DestDir%, ByVal uName$) ' Открыть таблицы и выбрать индексы Set FilesTable = MyDB.OpenRecordset("Files", dbOpenTable) FilesTable.Index = "idxFiles" MyWS.BeginTrans Set MovesTable = MyDB.OpenRecordset("Moves", dbOpenTable) fName$ = LCase$(fName$) MovesTable.Index = "idxMoves" ' Добавить новую запись в Files, если Seek (поиск) не дал результатов with FilesTable .Seek = "-". fName$ .LockEdits = False If .NoMatch Then ' если файла нет в базе данных, то .AddNew Else .Edit End If ' Записать данные о перемещениях в таблицу Files !filename = fName$ !mFrom = Path% !mTo = DestDir% !Date = Date$ !Time = Time$ !mBy = uName$ .Update End With ' Записать данные о перемещениях в новую запись MovesTable With MovesTable .AddNew !filename = fName$ !mTo = DestDir% !mFrom = Path% !MovedBy = uName$ !mDate = Date$ !mTime = Time$ .Update End With ' Закончить MyWS.CommitTrans dbClose End Sub
Список параметров в предложении "Sub" имеет два аспекта, о которых следует упомянуть. Первый состоит просто в том, что подпрограмма получает параметры. При объектно-ориентированном подходе класс cfDBsvr должен был предоставить ряд свойств Filename, Path, DestDir и др., значения которых OLE-клиент должен был устанавливать перед вызовом метода RecordMove этого класса. Такой метод потребовал бы дополнительного непроизводительного использования средства автоматизации OLE и к тому же лишнего обращения к RPC при каждой установке свойств, т. е. был бы значительно медленнее, чем просто передача всех параметров в одном обращении к методу RecordMove этого класса.
Другой аспект, заслуживающий упоминания, состоит в том, что все параметры описываются ByVal (предаваемые значениями), а не ByRef (по имени). При работе с локальным OLE-сервером передача параметров по имени была бы более эффективной, поскольку по сети передавался бы только адрес данных клиента. Но при работе с Remote Automation сервер не имеет доступа к содержимому памяти клиента. Параметр, заданный именем, все равно приходится передавать по сети серверу и затем копировать обратно, независимо от того, действительно ли изменила его подпрограмма на сервере. Когда же параметры передаются значениями, нужно лишь один раз перенести данные на сервер.
Переместив существенную часть исходной подпрограммы RecordMove на сервер OLE Atomation, я смог значительно сократить ее в новой программе клиента, не измен параметров или команд ее вызова из других подпрограмм:
Sub RecordMove(fName$, Path$, DestDir$, uName$)
Set MyDBobj = New cfDBsvr
MyDBobj.RecordMove fName$, GetQueueNum%(Path$),
GetQueueNum%(DestDir$), uNames
Set MyDBobj = Nothing
End Sub
Затем я принялся за подпрограмму SearchDB. В своем первоначальном виде эта подпрограмма передавала содержание текстового поля (Text1) в качестве параметра в сохраняемую переменную QueryDef. В текстовом поле хранилась спецификация файла, вводимая пользователем, и из QueryDef передавались обратно имена и маршруты доступа всех файлов из таблицы файлов (Files) в соответствии с этой спецификацией.
Получив эту информацию, подпрограмма просматривала каждую присланную запись и добавляла поля в блок списка найденных файлов, как показано ниже:
Sub SearchDB ()
Test$ = Text1
' открыть SnapShot Query, используя сохраненный параметр QueryDef
Dim MyQuery As QueryDef, SN as Snapshot
Set MyQuery = Db.OpenQueryDef("AName")
MyQuery!MyParam = Test$
Set SN = MyQuery.CreateSnapshot()
SN.MoveFirst
' Цикл по Snapshot
Do While Not SN.EOF
'...
'... команды перемещения данных из каждой записи SN в блок списка
'... найденных файлов, удаляемого для экономии пространства
'...
SN.MoveNext
Loop
End Sub
От сервер-версии подпрограммы RecordMove никак не требовалось, чтобы она передавала значение клиенту OLE, но подпрограмма SearchDB обязательно должна была это делать. Понятно, что, когда эта функция была разделена, серверу пришлось пересылать результаты запроса Snapshot программе-клиенту. Главное, что требовалось решить - в какой форме должны быть представлены полученные результаты запроса. О представлении Snapshot ("моментальный снимок") не могло быть и речи, так как при этом в программу-клиент пришлось бы включить объект доступа к данным, что потребовало бы загрузки библиотек баз данных VB. Более того, документация по средствам автоматизации OLE специально предостерегает от передачи Jet DAO по сети. Там же настоятельно рекомендуетс вместо этого передавать небольшие порции результатов запроса в виде вариантного массива (Variant), что я и сделал. Новый метод SearchDB в классе cfDBsvr сервера OLE Automation вызывает сохраняемый запрос, затем просматривает набор результатов, копируя два поля из каждой полученной записи в массив Variant в форме строки, разделенной запятыми (лист. 3).
Лист. 3. Здесь показан новый метод SearchDB в классе cfDBsvr сервера OLE Automation.Public function SearchDB(ByVal test$) As Variant ' Открыть Parameter Query Dim tmp() As Variant. SN As Object Set ParmQD = MyDB.QueryDefs("A Name") ParmQD!MyParam = test$ Set SN = ParmQD.OpenRecordset(dbOpenDynaset) ' Используя полученный набор результатов With SN On Error Resume Next ' Сосчитать полученные записи .MoveLast On Error GoTo 0 recCount% = .RecordCount If recCount% = 0 Then Exit Function ' Переписать полученные записи в массив Variant в виде строк, ' ограниченных запятыми ReDim tmp(recCount%) .Move First For Counter% = 1 To recCount% tmp(Counter%) = !filename & comma$ & !Path + comma$ & Y% .MoveNext Next Counter% Fnd With ' Закончить SearchDB = tmp() Set ParmQD = Nothing Set SN = Nothing End Function
В то же время клиентскую часть подпрограммы SearchDB я переделал таким образом, чтобы она вызывала метод SearchDB класса cfDBsvr, присваивая вычисленный им набор значений массив Variant под названием Test. Если метод SearchDB находит запись, соответствующую введенной пользователем спецификации файла, Test будет строковым массивом. Если же такие записи не найдены, выполнение подпрограммы заканчивается. Подпрограмма SearchDB использует функцию VarType, чтобы определить, является ли Test строковым массивом. Если да, то SearchDB продолжает просматривать массив, извлека присланные данные из каждой записи и добавляя их к блоку списка найденных файлов:
Private Sub SearchDB()
' Вызвать метод SearchDB в cfDBsvr
Dim MyOwnBob as cfDBsvr, test As Variant
On Error GoTo 0
' Удостовериться, что переданный набор не пуст
test = MyOwnBob.SearchDB(Text1)
If VarType(test) < (vbArray + vbString) Then Exit Sub
' Просмотреть переданный набор
found% = UBound(test)
For counter% = 1 to found%
a$ = test(counter%)
'...
'... каждая запись в массиве - это
'... строка, разделенная запятыми,
'... содержащая запрошенные поля,
'... поэтому проанализировать a$ и выполнить директиву
'...
Next
End Sub
Прежде чем перенести на сервер средства обращения к данным, используемые формой Copyflow - Get Info, мне пришлось решить ряд специфических проблем. В начальном варианте форма содержала два вида средств управлени данными, множество связанных текстовых полей и несколько SQL-операторов для получения и вывода на экран информации о файле, включая его текущий адрес и перечень всех его перемещений по сети. Так что дл перенесения программ доступа к данным, принадлежащих этой форме, на сервер OLE Automation мне пришлось "развязать" поля Text, удалить средства Data Control и добавить в текст клиентской части подпрограммы команды для заполнения полей Text, используя данные, присланные сервером.
Другая проблема состояла в том, что в начальной версии, внеся изменеия в записи, относящиеся к источникам информации и в соответствующие средства управления данными, подпрограмма могла проверить содержимое связанного поля, содержащего имя файла, дл которого предоставлялась информация (на лист. 4 показана соответсвующая часть текста подпрограммы). Если это поле оказывалось пустым, т. е. в таблице Files отсутствовала запись для данного файла, подпрограмма добавляла новую запись в таблицу использу сопряженные средства управления данными.
Лист. 4. Старый и новый текст формы Get Info программы Copyflow.ORIGINAL LOADSEQUENCE: Sub LoadSequence () ' Использовать средства управления данными для поиска ' информации о файле в FileDef$ Data1.RecordSource = SQL$ + FileDef$ Data1.Refresh MovesData.RecordSource = MovesSQL$ + FileDef$ Moves Data.Refresh x%=DoEvents() ' Если связанное поле имени файла пусто, добавить новую ' запись в таблицу Files If Labe14(0) = "" Then Data1.Recordset.AddNew ' ... ' ... Заполнить связанные поля для новой записи ' ... Data1.Recordset.Update ' Перезапустить подпрограмму Data1.Refresh End If ' ... ' ... Заполнить несвязанные элементы управлени ' дополнительными данными ' ... End Sub NEW SERVER-SIDE LOADSEQUENCE: Public Function GetFilelnfo(ByVal InfoRequestFile$) As Variant Dim Moves() As Variant. mm as Object FileDef$ = ExtractFileName(InfoRequestFile$) ' в новой подпрограмме два элемента управления данными ' начальной версии одним замещены сохраняемым элементом ' запроса по параметрам; этот запрос извлекает поля из ' таблиц Files и Moves: ' Stored QUERYDEF named FileAndMoves: ' PARAMETERS myfile Text; ' SELECT Files.Path, Files.Issue, Moves.* ' From Files, Moves ' WHERE Files.FileName=[myfile] And ' Files.FileName=Moves.FileName; ' выдать запросы Set ParmQD = MyDB.QueryDefs("FileAndMoves") ParmQD!myfile = FileDef$ Set mm = ParmQD.OpenRecordset(dbOpenDynaset. dbReadOnly) ' Сосчитать записи в полученном наборе данных With mm On Error Resume Next .MoveLast On Error GoTo 0 recCount% = .RecordCount ' Изменить размер вариантного массива на размер, ' равный числу записей +3, оставив место для данных ' из таблицы файлов в первых двух элементах массива, ' заполнить оставшиеся элементы данными из таблицы ' Moves If recCount% <> Then ReDim Moves(recCount% + 3) Moves(1) = !Path Moves(2) = !Issue For Counter% = 2 To recCount$ + 2 Counter$ = Counter% + 1 Moves(Counter%) = !mFrom & comma$ & !mTo & comma$ & !MovedBy & comma$ & !mDate & comma$ & !mTime .MovePrevious Next GetFileInfo = Moves() End If End With ' Закончить mm.Close Set mm = Nothing Set ParmQD = Nothing End Function NEW CLIENT-SIDE LOADSEOUENCE: Private Sub LoadSequence() ' Вызвать метод GetFileInfo Dim FileData As Variant. arrSize% FileData = MyDBobj.GetFileInfo(InfoRequestFile$) ' Если полученный результат не строковый массив, ' вызвать RecordMove для создания начальной записи дл ' файла, затем снова вызвать GetInfoFile If VarType(FileData) < (vbArray + vbString) Then Screen.MousePointer = 0 Set MyDBobj = Nothing Set FileData = Nothing x% = DoEvents() Set MyDBobj = Nem cfDBsvr MyDBobj.RecordMove ExtractFileName$(InfoRequestFile$), 99, GetOueueNum%(ExtractFilePath$(InfoRequestFile$)), uName$ Set MyDBobj = Nothing x% = DoEvents() x% = DoEvents() x% = DoEvents() Set MyDBobj = Nem cfDBsvr FileData = MyDBobj.GetFileInfo(InfoRequestFile$) End If ' Извлечь данные из полученного массива Variant arrSize% = UBound(FileData) fPath$ = FileData(1) fIssue$ = FileData(2) For x% = 3 To arrSize% MoveData = FileData(x%) If MoveData = "" Then ' ... ' ... заполнить поля в окне GetInfo ' ... End If Next End Sub
В новой версии этой подпрограммы не должно было остаться средств урпраления и мне пришлось найти другой способ управления информационными запросами к файлам, записи о которых Copyflow не имела. Поэтому в новой клиентской части программы используется функци VarType, чтобы определить, содержит ли массив Variant, при первоначальном обращении к методу GetFileInfo класса cfDBsvr, пустой набор даных. Если да, то функци VarType вызывает метод RecordMove этого класса, чтобы создать запись для файла, прежде чем снова обращаться к методу GetFileInfo.
Когда разделение было проведено, я скомпилировал новую программу-клиент и сервер OLE Automation обе в 32-разрядном варианте и запустил их на одной рабочей станции в среде Windows 95. Удостоверившись, что связи между ними функционируют нормально, я перенес программу-клиент на другую рабочую станцию, также работающую под управлением Windows 95. Эти рабочие станции были соединены кабелями Ethernet, и на обеих выполнялась программа Client for Microsoft Networks.
Затем я запустил Automation Manager на ПК, на который был загружен сервер OLE Automation дл обеспечения дистанционных запросов к этой станции, и запустил Remote Automation Connection Manager на обоих компьютерах. На ПК-сервере я использовал Connection Manager для передачи дистанционных запросов к серверу OLE - части поделенной программы Copyflow. Со стороны клиента я использовал Connection Manager дл определения положения в сети программных средств сервера. Тестирование снова показало, что связи между клиентом и сервером функционировали великолепно, и был рад констатировать, что перекрестная сетевая связь совсем (или почти) не повлияла на производительность.
К сожалению, тесты с 16-разрядной версией ПО клиента прошли не столь гладко. Взаимодействие 16-разрядных программ клиента с локальным 32-разрядным сервером иногда проходило вполне нормально, но время от времени, связь установить не удавалось. Ушло почти две недели нескончаемого тестирования, бесчисленных реинсталляций, десятки жалобных призывов о помощи ко всевозможным электронным форумам и несколько запросов в службу технических консультаций, чтобы выяснить источник проблемы: неизвестная ранее ошибка, не позволяюща 16-разрядным клиентам устанавливать связь с 32-разрядными серверами через NetBEUI, - протокол, который клиенты Microsoft Networking используют в среде Windows for Workgroups и Windows 95.
На данный момент компания Microsoft признала свою недоработку, но не смогла предложить какое-либо решение. Microsoft утверждает, что связи между 16- и 32-разрядным объектами работают безупречно в сочетании с протоколом TCP/IP, NetWare LANs и всеми другими преверенными фирмой сетевыми протоколами.
Ясно, что, прежде чем вынести окончательное заключение о производительности новой разделенной версии Copyflow по сравнению с более ранней, необходимо произвести дальнейшие испытания в больших масштабах. Но предварительные результаты обнадеживают.
Как и ожидалось, повышение эффективности запросов данных программой-клентом в сочетании с малым объемом порций ответных данных почти устраняет влияние пропускной способности сети на работу программы Copyflow и сокращает общую загрузку сети. Типичный ответ на запрос поиска файла Find File составляет от 10 до 15 байт на файл, а ответ на запрос Get Info состоит в среднем из 30 байт.
Устранение зависимости программы-клиента от библиотек баз данных Visual Basic также, как и ожидалось, улучшило работу. Суммарный объем исполнимого файла и библиотек, необходимых во время выполнени версии Copyflow на языке Visual Basic 3.0 (включа библиотеки баз данных) и версии на Visual Basic 4.0 (которая не содержит библиотек баз данных, но добавляет библиотеки OLE и RPC), составляет около 2 Мбайт в каждом случае. Однако реальный "чистый" объем памяти меньше, дело в том, что практически все связанные с OLE библиотеки, необходимые для Visual Basic 4.0, загружаются и используются также и редактором Word for Windows.
Когда вы вызываете программу, для которой требуютс библиотеки DLL, уже используемые другим приложением, Windows не загружает второй экземпляр DLL; она просто увеличивает число пользователей первого экземпляра. Таким образом, использование программой Copyflow библиотек OLE, которые пакет Word for Windows - приложение, с которым чаще всего работает Copyflow, - уже загрузил в память, не требует или почти не требует дополнительных затрат памяти.
Хотя библиотеки OLE и RPC требуют своих вычислительных ресурсов, их влияние оказываетс незначительным и предсказуемым. Напротив, воздействие на производительность передач больших наборов данных по сети, и без того предрасположенной к перегрузкам, была совершенно непредсказуемой.
Приемлемое время выполнения перекрестных сетевых операций новой версией Copyflow - в пределах 2-3 с, против 2-45 с (в зависимости от характера операции и загруженности сети) в случае старой версии - дает возможность более уверенно планировать другие усовершенствования. Несомненно, повышение эффективности работы программы с базой данных позволяет задуматься о расширении диапазона функций работы с данными Copyflow, что раньше было просто невозможно себе представить из-за проблем, вызванных низким быстродействием старой, неразделенной версии программы.
Концепция разделения предлагает также возможности дальнейшего повышения производительности Сopyflow. Например, было бы относительно просто создать промежуточный сервер OLE, который мог бы работать на рабочей станции клиента с вполне определенной целью: приема от программ клиента запросов на записи и их пересылки удаленному серверу OLE, когда он не занят другими запросами. Для пользователя это означало бы практически полное устранение задержек во врем операций записи данных, поскольку программа, выполняемая на рабочей станции клиента, не приостанавливалась бы в ожидании установления связи с сервером. Конечно, любая отсрочка записи представляет некоторую угрозу сохранности ваших данных, но в большинстве случаев фактическая задержка при использовании такой процедуры составит не более нескольких секунд, что сводит риск к минимуму, при этом устраняется досадное отставание отклика.
Есть также несколько путей повышени производительности серверной части программы. Можно создать совокупность программ-серверов с помощью приложения Pool Manager, чтобы распределить работу между разными серверами. С другой стороны, я мог бы создать разные серверы для выполнения каждой из трех операций доступа к данным, таким образом выполнение более быстрой операции, например, RecordMove, не задерживалось бы более медлительным поиском по запросу GetFileInfo.
Без средств автоматизации OLE единственной альтернативой применения инструмента Jet с его ничтожной сетевой производительностью для меня было бы использование полной СУБД клиент-сервер, что по отношению к этой относительно простой программе, оперирующей с небольшими объемами данных, было бы подобно стельбе из пушки по воробьям. Система дистанционного управления, позволившая программе Copyflow более простым и гибким способом избавиться от проблем с производительностью, сулит неплохие перспективы другим приложениям, производительность которых можно повысить, используя разделенную архитектуру.
ОБ АВТОРЕ: Пол Боннер - независимый журналист, проживающий в Бостоне, а также консультант по системам и специалист по визуальному программированию. С ним можно связаться по адресу E-mail: pbonner@zd.com