Отдалечени извиквания с .NET Remoting Автор
Виктор Живков
Необходими знания -
Базови познания за .NET Framework
-
Базови познания за езика C#
-
Базови познания по компютърни мрежи и Интернет технологии
-
Базови познания по разпределени архитектури и системи
-
Познания за сериализацията в .NET Framework
Съдържание -
Какво е .NET Remoting?
-
Кога се използва Remoting?
-
Remoting инфраструктурата
-
Remoting канали и форматери. Регистрация на канал
-
Активация на обекти. Активация от сървъра. Активация от клиента
-
Маршализация (Marshaling). Marshal-by-Value обекти. Marshal-by-Reference обекти
-
Живот на обектите (Lifetime). ILease
-
Remoting конфигурационни файлове
-
Практика: Създаване на Remoting сървър и клиент
-
Проблемът с общите типове
В тази тема ...
В настоящата тема ще разгледаме инфраструктурата за отдалечени извиквания, която .NET Framework предоставя на разработчиците. Ще обясним основите на Remoting технологията и всеки един от нейните компоненти. Ще започнем с канали и форматери и ще продължим с отдалечените обекти и тяхната активация. Ще се спрем на разликата между различните типове отдалечени обекти, жизнения им цикъл и видовете маршализация. Стъпка по стъпка ще достигнем до създаването на примерен Remoting сървър и клиент. Ще завършим с обяснение на един гъвкав и практичен начин за конфигуриране на цялата Remoting инфраструктура.
Разпределени приложения
Поради сложния характер на днешните приложения по-голямата част от тях са разпределени. Състоят се от няколко отделни компонента, които често пъти са отдалечени един от друг, но за целта на приложението трябва да взаимодействат помежду си. Съществуват няколко утвърдени от практиката модели за разпределени приложения:
-
Клиент/сървър – двете страни комуникират помежду си чрез заявки. Клиентът изпраща заявка към сървъра, сървъра я обработва и връща резултата обратно.
-
Разпределени обекти (distributed objects) – примери за такива инфраструктури за:
-
DCOM – използва се в Microsoft Windows
-
CORBA – отворен стандарт, който за съжаление е много сложен
-
Java RMI – стандарт базиран на Java технологията
-
.NET Remoting – стандарт предлаган от .NET Framework
-
Уеб услуги (Web Services) – стандартизирана технология, при която отделни обекти на различни платформи и системи комуникират посредством стандартни SOAP съобщения.
По-нататък в настоящата тема ще разгледаме подробно само .NET Remoting технологията. Няма да правим сравнение между различните модели, такива тъй като тази тема не е обект на настоящата книга. Уеб услугите в .NET Framework са разгледани подробно в предишната глава. [TODO: Add cross-reference to Web Services chapter]
Какво е .NET Remoting?
Remoting технологията в .NET Framework предлага на разработчика прозрачен достъп до отдалечени обекти, без излишни трудности и загуба на гъвкавост. Подходяща е за случаи, когато се налага да се достъпват обекти, които:
-
се намират в друг домейн на приложението (application domain)
-
принадлежат на друг процес
-
се намират на отдалечена машина
Независимо от местоположението на интересуващите ни обекти Remoting инфраструктурата осигурява лесен начин за използването им. Комуникацията между обектите се извършва чрез стандартизиран механизъм и специалната инфраструктура, осигурени от .NET Framework.
Кога се използва Remoting?
Тъй като Remoting технологията ни позволява да осъществим достъп до и реализация на отдалечени обекти, тази технология може да бъде решение за проблеми, свързани с мрежова комуникация. Самата инфраструктура е гъвкава и разширяема. Тя дава решения на голям набор от проблеми без особено усилия от страна на програмиста. .NET Framework ни предлага и алтернативи в лицето на Web услугите и мрежовата комуникация на по-ниско ниво (виж System.Net). Повече за това, кога е удачно да използваме Remoting, можете да намерите в частта "Remoting сценарии".
Microsoft Indigo
Друга зараждаща се технология с голям потенциал e Microsoft Indigo. Тя включва в себе си възможностите на ASMX (или т. нар. уеб услуги), .NET Remoting, Enterprise Services, WSE (Web Services Enchancements) и MSMQ (Microsoft Message Queue). В Indigo е доразвита концепцията за комуникация между отдалечени обекти, като се набляга на съвместимостта на различните среди и платформи. Архитектурата на Indigo е пример за имплементация на набиращата популярност Service Oriented Architecture (SOA). Заради развитието на тази технология в .NET Framework 2.0 ще има промени, свързани със самия Remoting. Обратната съвместимост е запазена, но Microsoft препоръчват преминаване към Indigo, след като излезе финалната му версия.
Remoting инфраструктурата
Remoting инфраструктурата се състои от:
-
канали – канали, по които се пренасят данните и съобщенията от и към отдалечените обекти
-
форматери – отговарят за форматирането, кодирането и декодирането на съобщенията, които се пренасят чрез каналите в някакъв формат
-
прокси обекти – предават извикванията на методи към и от отдалечените обекти
-
механизми за активация – служат за създаване и получаване на инстанция на отдалечения обект
-
маршализация – осигурява пренос на обекти, техните свойства, полета и т.н.
Като нагледен пример за това каква е взаимовръзката межди тези компоненти и как работят те, нека разгледаме следната диаграма:
Как работи Remoting инфраструктурата?
При създаването на инстанция на отдалечен обект Remoting инфраструктурата автоматично създава при клиента прокси клас, който се грижи за обмяната на съобщенията между клиентския и сървърния обект. За клиента съществуването му остава незабележимо, тъй като всяко извикване се обработва автоматично от средата. След като проксито получи контрола на съобщението, го предава на следващия възел в инфраструктурата – канала. Каналът работи тясно свързано с форматер, който форматира данните, обменяни между клиента и сървъра. След това съобщението се изпраща по TCP сокет до получателя, десериализира се, сървърният обект извършва извикването и връща резултата по обратния път към клиента. Всяка от описаните стъпки, с изключение на създаването на сървърно прокси, може да бъде настроена допълнително, така че да отговаря на специфични нужди.
Remoting канали
Каналите се използват за пренос на съобщения от и към отдалечени обекти. Когато клиентът извика метод на отдалечен обект, параметрите, както и всички детайли, свързани с даденото извикване, се транспортират през канал до обекта. Резултатът от извикването се връща обратно при клиента по същия начин. Всеки клиент може да избере към точно кой канал от регистрираните на сървъра да се прикачи, за да комуникира с отдалечения обект. Това дава изключителна гъвкавост на разработчика в избора на канал, така че той да отговаря на специфичните нужди.
За канали могат да се използват стандартните вградени канали, техни модификации и дори напълно нови, създадени от разработчика канали, които да комуникират по избран протокол. В .NET Framework са реализирани три типа канали:
-
TCP канал – използва се чист TCP сокет. Данните се сериализират стандартно посредством бинарен форматер и се транспортират посредством TCP протокол. При нужда форматерът може да бъде преконфигуриран като SOAP.
-
HTTP канал – използва се SOAP протокол. Съобщенията се сериализират чрез SOAP форматер в XML с включени SOAP хедъри. Може да се конфигурира да използва бинарен форматер вместо стандартния SOAP. Комуникацията между обектите се извършва посредством HTTP протокол и се използва модела на заявка/отговор (request / response) подобно на този в уеб приложенията.
-
Други канали – дефинирани от разработчика. Те трябва да имплементират интерфейсите: IChannel, IChannelReceiver и/или IChannelSender. Както се вижда от имената на последните два интерфейса, първият служи за канал, който може само да приема съобщения, а вторият – за такъв, който може само да изпраща. В повечето случаи каналите имплементират и двата интерфейса, за да могат да комуникират в двете посоки.
Каналите се използват както от сървърните приложения, така и от клиентските. Каналът трябва да бъде регистриран и от двете страни преди началото на комуникация между тях. За преноса на потока от съобщения се използват TCP портове, през които се подават форматираните данни на извикването.
Регистрация на канал
Регистрацията на канал е задължителна стъпка в подготвянето на комуникацията с отдалечени обекти. Този процес има следните изисквания и ограничения:
-
Най-малко един канал трябва да бъде регистриран преди да бъде направено обръщение към отдалечен обект. Каналите задължително се регистрират преди регистрацията на самите обекти.
-
Всеки канал се регистрира за даден application domain. В един процес е възможно да има няколко application domains. В момента, в който дадения процес приключи работата си, всички канали, които са регистрирани в него, се унищожават автоматично.
-
В рамките на даден application domain всеки канал трябва да има уникално име. Разгледайте примера за да видите начина, по който можете да зададете уникално име на канал:
IDictionary properties = new Hashtable();
properties["name"] = "http1";
properties["port"] = "9001";
ChannelServices.RegisterChannel(
new HttpChannel(properties, null, null));
| -
Не е възможно два или повече канала да бъдат регистрирани върху един и същ порт, по едно и също време, на една и съща машина. От друга страна е възможно един канал да бъде регистриран на повече от един порт.
-
В случай, че не сте сигурни кой порт да използвате за канала, можете да накарате Remoting инфраструктурата да ви предостави автоматично свободен порт като регистрирате канал за порт 0.
-
Клиентът може да комуникира с отдалечения обект, използвайки който и да е регистриран канал. Remoting инфраструктурата автоматично осигурява свързването на отдалечения обект с правилния канал. Клиентът е задължен да регистрира канал преди да се опита да комуникира с отдалечения обект.
Пример за регистрация на канал
Регистрацията на канал може да се извърши по два начина:
-
Чрез класа System.Runtime.Remoting.Channels.ChannelServices:
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
...
TcpChannel channel = new TcpChannel(1234);
ChannelServices.RegisterChannel(channel);
| -
Чрез класа System.Runtime.Remoting.RemotingConfiguration и конфигурационен файл:
-
RemotingConfiguration.Configure("MyClient.exe.config");
|
Конфигурационните файлове, като средство за настройка на Remoting инфраструктурата, ще разгледаме малко по-късно в настоящата тема.
Форматери (formatters)
Ролята на форматерите в Remoting инфраструктурата е да сериализират съобщенията между двете страни в определен формат. На разположение на разработчиците са два вградени форматера плюс възможността да създадат свой собствен форматер, който да отговаря на специфичните им нужди.
-
SOAP форматер – сериализира поток в SOAP съвместим XML формат. Използването SOAP протокола позволява съвместимост с множество платформи и клиенти. Поради употребата на XML, обемът на предаваните данни е голям и това води до намаляване на производителността.
-
Бинарен форматер – сериализира поток в двоичен формат, който е с много по-малък обем от SOAP варианта. Поради вида на сериализирания поток не е толкова съвместим с различни системи, колкото е SOAP потока.
-
Друг форматер – форматер, реализиран от разработчика за неговите специфични цели и нужди.
Форматери по подразбиране
Всеки от предоставените от .NET Framework канали има форматер по подразбиране. На разработчика се предоставя готово решение със системата от класове за осъществяване на отдалечените извиквания и той може да се съсредоточи върху реализацията на програмната логика. Двата канала, които са вградени в Remoting инфраструктурата, имат форматери по подразбиране:
-
TCP каналът има бинарен форматер.
-
HTTP каналът има SOAP форматер.
Тези форматери могат да се преконфигурират. Ето един пример как можете да реализирате в своето приложение TCP канал със SOAP форматер:
SoapServerFormatterSinkProvider provider =
new SoapServerFormatterSinkProvider();
IDictionary properties = new Hashtable();
properties["port"] = 12345;
TcpChannel channel = new TcpChannel(properties, null, provider);
|
Друг начин за промяна на стандартния форматер на вградените канали е чрез редактиране на конфигурационния файл machine.config. Този начин е разгледан подробно в частта описваща конфигурирането на Remoting инфраструктурата в края на темата.
Активация на обекти
Процесът на създаване на инстанция на отдалечен обект се нарича активация. Remoting инфраструктурата предоставя два начина за извършването й:
-
Сървърна активация (server-side activation) – използва се в случаи, когато няма необходимост отдалечените обекти да поддържат своето състояние между две извиквания (SingleCall) или когато множество клиенти извикват методи на една и съща инстанция на обекта и трябва да се поддържа състоянието на обекта между извикванията (Singleton).
-
Клиентска активация (client-side activation) – инстанцията на обекта е създадена само за клиента, който я е извикал. Само той я управлява и определя времето й за живот. Жизненият цикъл на този вид отдалечени обекти се управлява чрез lease-based система на инфраструктурата.
Преди да е възможна активацията на обекти, е задължително да бъдат регистриран всеки от типовете на отдалечените обекти, които смятаме да използваме.
SingleCall режим на активация
SingleCall режимът е приложим при обекти със сървърна активация. Характерно за този режим е, че при всяко извикване на метод от страна на клиента се създава нова инстанция на обекта на сървъра, която обслужва извикването и веднага след това завършва своя жизнен цикъл.
Такива обекти са удобни в случай, че имаме метод, който трябва да свърши определена работа, за извършването, на която нямаме нужда от предишното състояние (state) на обекта. Обекти с такъв режим на работа улесняват балансирането на натоварването (load-balancing) на сървъра, тъй като не се налага да се поддържа състоянието им между отделните извиквания.
Singleton режим на активация
Singleton режимът е приложим при обекти със сървърна активация. Характерно при него е, че съществува единствена инстанция на отдалечения обект и всички клиенти работят с нея. Нейните данни, методи и свойства са споделени между всички клиенти. Самият обект поддържа своето състояние (state).
Този режим на активация е подходящ, когато данните на обекта, трябва да бъдат споделени или когато инстанцирането и поддържането на състоянието на обекта създава допълнително натоварване. При използването на Singleton обекти е възможно няколко клиента едновременно да работят с тях, поради което трябва да се осигури синхронизация при достъпа до общи данни. [TODO: cross reference to multitreading chapter]
Клиентска активация
При активация от страна на клиента, след заявката на сървъра се създават инстанции на отдалечения обект. Създаденият обект обслужва единствено своя клиент. Обектът може да поддържа своето състояние между извикванията. Жизненият цикъл на всяка инстанция се контролира чрез lease-based системата на Remoting инфраструктурата (ще я разгледаме подробно в частта "Живот на обектите" [TODO: линк]).
При всяка заявка за създаване на нов обект се връща нова инстанция. С такъв тип обекти трябва да се работи внимателно, тъй като е възможно за един клиент на сървъра да има повече от една инстанция. При проектиране на такива класове трябва да се отдели време за оптимизиране на методите на класа и да се направи преглед на това колко и какви данни съдържа той, за да се облекчи натоварването на сървъра.
Регистрация на отдалечен обект
Регистрацията на отдалечените обекти следва веднага след регистрацията на Remoting канали. За да извършим това ни е необходимо да предоставим на Remoting инфраструктурата следните данни:
-
името на асемблито, в което се намира класа, който искаме да регистрираме
-
типа на отдалечения обект
-
URI (Unique Resource Identifier) на обекта, чрез който клиентът да може да се обръща към него
-
в случай, че обектът е със сървърна активация – неговия режим на активация (SingleCall или Singleton)
Отново имаме избор между два начина за регистриране – чрез код или чрез конфигурационен файл.
Чрез регистрацията предоставяме данни, които описват уникално на Remoting инфраструктурата всеки тип, който искаме да използваме като отдалечен. Този процес на регистрация е задължителен както за сървъра така и за клиента. Важен момент при регистрирането на типовете е регистрациите при сървъра и клиента да си съответстват. Друго изискване е, типовете, които се използват за отдалечени обекти на сървъра и тези на клиента да имат една и съща версия или поне техните интерфейси да съвпадат. Този конкретен проблем е разгледан в частта "Проблемът с общите типове". [TODO: link]
Регистрация на сървъра (Server-Side Registration)
Чрез програмен код имаме три възможности за регистриране на типа на обект, който искаме да е достъпен отдалечено. Използват се методите на класа RemotingConfiguration:
-
RegisterWellKnownServiceType(…) с параметър, указващ че обектът трябва да се използва в SingleCall режим – регистрира тип на отдалечен обект в режим SingleCall. Пример:
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(CommonTypes.Library), // type of the remote object
"Library", // assembly name
WellKnownObjectMode.SingleCall); // activation mode
| -
RegisterWellKnownServiceType(…) с параметър указващ че обектът трябва да се използва в Singleton режим – регистрира тип на отдалечен обект в режим Singleton. Пример:
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(CommonTypes.Library), // type of the remote object
"Library", // assembly name
WellKnownObjectMode.Singleton); // activation mode
| -
RegisterActivatedServiceType(…) – регистрира отдалечен обект с клиентска активация. Пример:
-
RemotingConfiguration.RegisterActivatedServiceType(
typeof(CommonTypes.Library)); //type of the remote object
| Регистрация на клиента (Client-Side Registration)
Чрез програмен код имаме два начина за регистриране на типа на обект, който искаме да ползваме отдалечено от някой Remoting сървър. Използват се методите на класа RemotingConfiguration:
-
RegisterWellKnownClientType(…) – регистрира тип със сървърна активация. Не се указва дали обектът използва режим SingleCall или Singleton, защото това зависи от сървъра. Клиентът не може и не трябва да знае такива подробности в имплементацията и логиката на сървъра. Форматът на URI адреса, който се подава на метода, е следният:
<протокол>://<име на сървъра или IP адрес>:<порт>/<път до асембли с типа>/<тип>
|
Ето един пример за регистрация на отдалечен тип с активация от страна на сървъра:
RemotingConfiguration.RegisterWellKnownClientType(
typeof(CommonTypes.Library), // type of the remote object
"http://remoting_server:1234/remoting/Library"); // object URI
| -
RegisterActivatedClientType(…) – регистрира тип с клиентска активация. Форматът на URI идентификаторът е следният:
<протокол>://<име на сървъра или IP адрес>:<порт>
|
Ето пример за регистрация на отдалечен обект, който ползва клиентска активация:
RemotingConfiguration.RegisterActivatedClientType(
typeof(CommonTypes.Library), // type of the remote object
"http://remoting_server:1234"); // URL of the server
| Създаване на инстанция на отдалечен обект
За да получим инстанция на даден отдалечен обект трябва преди това да сме регистрирали неговия тип в Remoting инфраструктурата. След това можем да създадем обект от този тип по някой от следните начини:
-
Чрез статичния метод GetObject(…) на класа Activator – използва се за отдалечени обекти със сървърна активация. Създава прокси обект при клиента, чрез който се осъществяват всички извиквания. Комуникация със сървъра се извършва единствено при извикването на методите и връщането на резултата, а не при създаването на прокси обекта.
Library library = Activator.GetObject(
typeof(Library), // type of the remote object
"http://remoting_server:1234/remoting/Library" // object URI
) as Library;
| -
Чрез оператора new – може да се използва за създаване на обекти със сървърна и с клиентска активация. Осигурява и механизъм за предаване на параметри при инстанциране. В C# операторът new се използва както при създаването на локални обекти, така и при създаването на отдалечени обекти. За да създадем отдалечен обект с new, преди това съответният клас трябва да е бил регистриран в Remoting инфраструктурата. В противен случай се създава локален обект. Ето примерен код, който създава отдалечен обект с оператора new:
// This class is located and registered on the server
class Library
{
private string mName;
// Default constructor
public Library()
{
mName = "Library";
}
// Parametrized constructor
public Library(string aName)
{
mName = aName;
}
}
...
// This code runs on the client-side
// Register the Library class as remote type
RemotingConfiguration.RegisterWellKnownClientType(
typeof(CommonTypes.Library), "http://remoting_server:1234/remoting/Library");
// Get object from the server using the default constructor
Library library1 = new Library();
//Get object from the server using the parameterized constructor
Library library2 = new Library("My library");
|
Кодът за инстанциране на отдалечен обект с ключовата дума new не се отличава от създаването на обикновени обекти. Изобщо работата с отдалечени обекти изглежда в повечето случаи, като че ли се изпълнява върху обикновени инстанции. След регистрация Remoting инфраструктурата се грижи всяко извикване да се прехвърля от локалния прокси клас към отдалечения обект прозрачно, без допълнителна намеса на програмиста.
Маршализация (Marshaling)
Маршализацията (marshaling) е процес на пренос на обекти и данни между Remoting клиента и сървъра. Този процес се извършва при всяко:
-
подаване на параметри на метод
-
връщане на резултат от метод
-
достъп до поле или свойство на обект или тип
Според начина, по който се пренасят има три типа обекти:
-
Marshal-By-Value обекти – пренасят се по стойност посредством сериализация. [TODO: add reference to Serialization chapter]
-
Marshal-By-Reference обекти – пренасят се по референция. По специалното при тях е, че се използва отдалечена референция.
-
Non-Marshaled обекти – не се пренасят. Причина за това може да бъде, че обектите стават невалидни извън своя application domain или контекст, поради съображения за сигурност и др.
Marshal-By-Value обекти
Marshal-By-Value са всички обекти, които са маркирани с атрибута [Serializable] и/или имплементират интерфейса ISerializable. Тези обекти се предават по стойност, т.е. тяхно копие се пренася до отдалечената машина. Копират се всички сериализируеми полета и свойства в обекта. Като поведение те са подобни на стойностните типове в .NET Framework. При промяна на данните се променя само локалното копие на обекта. Целият обект се пренася до отдалечената машина наведнъж (само с едно извикваме) и това осигурява добро бързодействие в случай, че обемът на данните не е голям.
Ето един пример, в който са дефинирани два класа, които се маршализират по стойност:
[Serializable]
public class Book
{
private string mAuthor;
private string mTitle;
// Default constructor
public Book()
{
}
...
}
public class Author : ISerializable
{
private string mName;
private string[] mBooks;
// Default constructor
public Author()
{
}
// Constructor for serialization only
private Author(SerializationInfo info,
StreamingContext context)
{
// Deserialize data to class fields
}
// ISerializable.GetObjectData method
public void GetObjectData(SerializationInfo info,
StreamingContext context)
{
// Serialize class fields
}
...
}
| Marshal-By-Reference обекти
Marshal-By-Reference са всички обекти, които са наследници на класа MarshalByRefObj без значение дали типът е сериализируем. Пренасят се до отдалечената машина посредством отдалечена референция, от която се създава динамично прокси клас. Всяко извикване или операция върху обекта се извършва върху прокси класа, който от своя страна прехвърля нужните данни към отдалечената инстанция, където се изпълнява реално извикването. Този процес се повтаря за всяко четене или писане в поле или извикване на метод, което значително понижава производителността.
[Serializable]
public class Book : MarshalByRefObject
{
private string mAuthor;
private string mTitle;
// Default constructor
public Book()
{
}
...
}
| Живот на обектите (Lifetime)
В .NET Framework жизненият цикъл на обектите се управлява от системата за събиране на боклука (Garbage Collector). Тя следи дали даден обект се достъпва от клиентите в даден application domain (AppDomain). Когато обектът спре да бъде използван той подлежи на събиране от системата за боклук. Ако обектите и клиентите им са в един и същ application domain, системата работи без проблеми. Дори в случай, че клиентът и обектите са в отделни домейни, но в рамките на един и същ процес, отново няма никакви проблеми, тъй като те всичките споделят общ управляван хийп (managed heap).
Случаят, в който клиентите и обектите са в различен процес и/или машина, е по-особен. Ако системата за събиране на боклука работи по същия начин, то тя веднага след инициализирането ще открие, че към обекта няма референции и веднага ще го обяви за боклук. За да реши този проблем Remoting инфраструктурата предлага "система за отпускане на „живот назаем” за обектите – lease-based lifetime management.
Живот назаем
При създаването си всеки отдалечен обект на сървъра се асоциира с Lease обект, който има задачата да контролира жизнения му цикъл. Той определя времето, за което отдалечения обект е активен и не подлежи на събиране от системата за системата за почистване на паметта.
Специален обект наречен Lease Manager следи сървърните обекти и техните асоциирани Lease обекти. Всеки Lease обект има начално време, което да дава назаем (initial lease time). Lease времето започва да тече след като първата референция към сървърния обект се маршализира през границата на домейна. Докато то е по-голямо от нула, се счита че обектът, асоцииран с него, все още е активен и CLR смята, че сървърния обект все още се използва от клиента. Lease мениджърът пази референция към обекта, за да предотврати неговото събиране от “системата за почистване на паметта”.
Когато Lease времето свърши, CLR решава че обектът не е вече в употреба и че той ресурсите му подлежат на освобождаване. При свършване на времето е възможно да има клиенти, които все още да се нуждаят от обекта. В този момент се отправя заявка към всеки от тях дали все още се нуждаят от обекта.
Спонсори на обектите
Всеки клиент, който иска да бъде "попитан" дали иска да се удължи живота на обекта, който той ползва, трябва да предоставя специален обект-спонсор. Спонсорът от своя страна решава дали да удължи живота на сървърния обект или не. Ако реши да удължи времето за живот, то Lease мениджърът изпълнява желанието му. В противен случай системата за събиране на боклук може да прибере обекта. Следващата схема обяснява нагледно процеса:
Функцията на всеки един от показаните обекти е обяснена по-долу. Тази схема за определяне на продължителността на живота е приложима само за Marshal-By-Reference обекти с клиентска активация и Singleton обекти със сървърна активация. Двата случая има специфични особености, които са разгледани по-долу. Сега ще насочим вниманието си към Lease обекта, спонсора и Lease мениджъра.
Lease обектът имплементира интерфейса ILease, който предоставя функции за определяне живота на асоциираните с него обекти. Конкретна имплементация на този интерфейс е класът Lease в System.Runtime.Remoting.Lifetime.
Интерфейсът ILease
Интерфейсът ILease се намира в пространството от имена System. Runtime.Remoting.Lifetime. Както показва схемата, обекти от този тип дават назаем живот на асоциирания си отдалечен сървърен обект. Remoting инфраструктурата автоматично задава стойности по подразбиране за свойствата на всяка инстанция от този тип, но е възможно и програмистът да зададе свои собствени. Свойствата, които интерфейсът предоставя са:
-
InitialLeaseTime – задава времето на живот на обекта. В случай че искаме да зададем друга стойност от тази по подразбиране, трябва да го направим задължително преди да сме активирали обекта. По подразбиране времето за живот е 5 минути.
-
RenewOnCallTime – задава времето, с което е възможно да се увеличи живота на обекта при всяко извикване. По подразбиране стойността му е 2 минути. Важно е да се отбележи, че не е възможно да се получи натрупване на време при интензивно ползване на обекта, тъй като формулата, по която се изчислява новото време за живот, е следната:
currentLeaseTime = MAX(InitialLeaseTime - expiredTime, RenewOnCallTime)
|
С други думи RenewOnCallTime стойността ще има ефект ако е по-голяма от оставащото време за живот, като в такъв случай тя определя оставащо време за живот. Отново сме задължени да зададем тази стойност преди активацията на обекта.
-
SponsorshipTimeout – задава времето, в което всеки спонсор трябва да отговори на изпратената му заявка за отпускане на още време. В случай, че не бъде получен отговор, се счита, че спонсорът е отказал. Ако нито един от спонсорите не даде допълнително време то обектът подлежи на унищожение. По подразбиране стойността е 2 минути.
-
CurentLeaseTime – връща колко време още остава от живота на обекта.
-
CurrentState – връща състоянието на живота на обекта, като стойност от изброения тип LeaseState. Стойностите, които връща са: Initial в процеса на активация; Active, когато оставащото времето за живот е по-голямо от 0; Renewing в процеса на добавяне на време при извикване; Expired в случай че времето за живот е изтекло; Null – при проблем с изчисляването на състоянието. Ето дефиницията на типа LeaseState:
[Serializable]
public enum LeaseState
{
Null = 0,
Initial = 1, // while initializing
Active = 2, // lease time greater than 0
Renewing = 3, // while renewing
Expired = 4 // lease time equal to 0
}
| Използване на ILease
В случай, че подразбиращите се стойности не са удобни, можем да променим глобално за цялото приложение стойностите чрез свойствата LeaseTime, RenewOnCallTime и SponsorshipTimeout на класа LifetimeServices (в пространството от имена System.Runtime.Remoting. Lifetime). Тъй като това са глобални настройки за поведението на отдалечените обекти, те трябва да бъдат направени преди регистрирането на отдалечените типове, които ще използваме. Например:
static void Main()
{
LifetimeServices.LeaseTime = TimeSpan.FromMinutes(10);
LifetimeServices.RenewOnCallTime = TimeSpan.FromMinutes(5);
LifetimeServices.SponsorshipTimeout = TimeSpan.FromMinutes(1);
// Register remotable types or load config file
}
|
Можем да променим стойностите на Lease обекта, асоцииран с конкретен отдалечен обект по следния начин:
// Type definition
public class ClientActivatedType : MarshalByRefObject
{
}
...
// Create and activate the first instance
ClientActivatedType caLongLiving = new ClientActivatedType();
// Get the Lease object associated with it
ILease longLiving = (ILease) RemotingServices.GetLifetimeService(caLongLiving);
// Adjust the lifetime parameters
longLiving.RenewOnCallTime = TimeSpan.FromMinutes(10);
longLiving.SponsorshipTimeout = TimeSpan.FromMinutes(1);
// Create and activate the second instance
ClientActivatedType caShortLiving = new ClientActivatedType();
// Get the Lease object associated with it
ILease shortLiving = (ILease) RemotingServices.GetLifetimeService(caShortLiving);
// Adjust the lifetime parameters
shortLiving.RenewOnCallTime = TimeSpan.FromMinutes(1);
shortLiving.SponsorshipTimeout = TimeSpan.FromSeconds(15);
|
Същите настройки могат да бъдат направени и посредством конфигурационен файл. Структурата и съдържанието на конфигурационния файл ще бъде разгледан в частта "Remoting конфигурационни файлове".
Интерфейсът ISponsor
Интерфейсът ISponsor се намира в пространството от имена System. Runtime.Remoting.Lifetime. Както споменахме по-рано спонсорът е обект, който има властта да удължава времето на живот на отдалечени обекти. За да може да изпълнява тази роля обектът трябва да имплементира интерфейса ISponsor. Единственият метод на този интерфейс е Renewal(ILease lease), който е дефиниран по следния начин:
public interface ISponsor
{
TimeSpan Renewal(ILease lease);
}
|
Lease мениджърът извиква този метод, когато животът на даден обект е изтекъл, за да поиска допълнително време.
За да добавим спонсор към даден отдалечен обект можем да използваме метода на ILease обекта Register(…). (За да получим обект от такъв тип трябва да извикаме метода GetLifetimeService(…) на желания отдалечен обект, на който ще добавяме спонсор.) Аналогично можем да премахнем даден спонсор от отдалечен обект чрез метода Unregister(…) на ILease.
// Activation of the object
Library library = new Library();
// Getting lease object associated with current object
ILease lease = RemotingServices.GetLifetimeService(library)
as ILease;
// Creating new sponsor
MySponsor sponsor = new MySponsor();
// Attaching sponsor to our lease object
lease.Register(sponsor);
// Detaching sponsor from our lease object
lease.Unregister(sponsor);
|
Премахването на спонсора, посредством метода Unregister(…), от списъка със спонсори на отдалечения обект не е задължително, но на практика, ако не се използва, води до голяма загуба на производителност, тъй като Remoting системата отправя заявки и чака отговор от всеки регистриран спонсор. Така че препоръчително е след като даден спонсор стане ненужен, да бъде изваден от списъка на валидните спонсори.
Важна особеност на класовете, които играят ролята на спонсори е, че те реално се извикват през границите на домейна на приложението. Поради това спонсорът трябва да бъде отдалечен обект, който се маршализира по стойност или по референция.
Спонсор, маршализиран по стойност
При спонсори, които са маркирани единствено като сериализируеми (с атрибута [Serializable]), при регистриране на спонсор той се маршализира по стойност до сървърната страна. От там нататък сървърът използва своето копие на спонсора. Това осигурява по-добро бързодействие тъй като спестява маршализирането на спонсора за всяко извикване. Този начин ни дава възможност да контролираме живота на обектите от гледна точка на натоварването на сървъра (което не винаги е показателно за това, дали обектът все още е нужен на клиента!). За съжаление при решаването дали да удължи живота на даден обект спонсорът може да използва само информацията, която има на сървъра, тъй като е отделен от клиентската част.
Спонсор, маршализиран по референция
В случай, че спонсорът наследява MarshalByRefObject, той се намира при клиента. Тъй като има достъп до клиентската част, той може да базира своите решения на информацията, която получава от клиента, като следи определени негови събития или свойства.
В този случай възниква следният въпрос: ако Lease обектът пази "жив" отдалечения сървърен обект, и ако спонсорът пази Lease обекта "жив", какво пази спонсора "жив"? Отговорът е, че при клиента трябва да се държи референция към спонсора и обикновено това се реализира чрез член-променлива на някой подходящ клас. По този начин клиентът има възможност да дерегистрира своите спонсори когато завършва изпълнението си. Това може да се осъществи и в метода Dispose() ако класът имплементира интерфейса IDisposable. Както вече споменахме дерегистрирането на спонсора подобрява значително производителността, тъй като Lease мениджърът не губи време да достъпва невалиден спонсор.
Друга характерна особеност на този тип спонсори е, че те са callback обекти от гледна точка на сървъра. Поради това и поради изисквания към сигурността в .NET за да работят този вид спонсори трябва да укажем при конфигурирането на каналите следните свойства:
-
Клиентът трябва да регистрира порт за всеки канал. Това е нужно за да може Lease мениджърът да може да извиква спонсора. По принцип няма значение точно кой порт ще бъде подаден. Добра практика е да се подава порт 0, тъй като в този случай Remoting системата сама избира някой порт. Каналът, номерът на порта и местонахождението на спонсора се изчисляват когато се маршализира референцията на спонсора до сървъра.
Живот на Singleton отдалечени обекти
Семантиката на Singleton обектите изисква те да имат неограничен живот. Посредством стандартните процедури за определяне на продължителността на живота в Remoting системата, това е невъзможно или най-малкото неудобно и неефективно. За да се реши този проблем при проектирането на Singleton обекта трябва да се предефинира метода InitializeLifetileService() на базовия клас MarshalByRefObject по следния начин:
public class MySingleton : MarshalByRefObject
{
public override object InitializeLifetimeService()
{
// Returning null as Lease object
// indicates that lease never expires
return null;
}
}
|
Връщайки null като резултат от метода ние указваме, че искаме този обект да има безкраен живот. По този начин също решаваме проблема със спонсорите и Lease свойствата определящи продължителността на живота на обекта.
Remoting конфигурационни файлове
В настоящата секция ще разгледаме конфигурационните файлове на Remoting инфраструктурата и ще дадем описание на таговете, които се използват в тях. Тъй като сървърът и клиентът имат свои специфични елементи ще ги разгледаме последователно.
Remoting конфигурационните файлове представляват XML файлове със специална структура. Работата с тях е лесна и най-важното – те предоставят гъвкав начин за конфигуриране на приложения без да се налага прекомпилиране (за разлика от варианта, в който настройките са зададени в програмния код). В практиката е силно препоръчително да конфигурацията да се държи в XML а не в програмния код.
За да накараме Remoting системата да използва този файл трябва да извикаме статичния метод RemotingConfiguration.Configure(…) с единствен параметър – пътят и името на конфигурационния файл:
RemotingConfiguration.Configure("remoting.config");
|
Пътят до файла, името и разширението му нямат значение, освен в случай на хостинг на асемблитата със споделените класове в IIS. Remoting конфигурационните файлове са допълнителни и са отделни от конфигурационните файлове на приложението.
След като сме извикали този метод можем да пристъпим към активиране и използване на отдалечените обекти без никакви по-нататъшни грижи за канали и форматери.
Структура и елементи на конфигурационния файл
В тази част от главата ще разгледаме подробно повечето от елементите, които могат да се съдържат в конфигурационния файл на приложение, използващо Remoting. Освен наличието на предефинираните XML тагове, в определени случаи е важно и тяхното разположение спрямо останалите тагове в конфигурацията. Ще започнем разглеждането от корена на XML документа – това е тагът .
Този таг е коренът на всички елементи във файла. Трябва да се среща точно веднъж.
Намира се задължително като под-елемент на . В този таг трябва да се намират всички елементи свързани с конфигурирането на Remoting инфраструктурата. Може да се среща само веднъж.
Представлява задължително под-елемент на . Съдържа всички специфични за приложението данни. Може да се среща само веднъж.
Има незадължителен атрибут name, който указва името на приложението:
Намира се винаги под елемента . Служи за конфигуриране на времето на живот на обектите. Важи за всички обекти на това приложение. Тези настройки се отнасят за обектите с клиентска активация и за Singleton обектите. Те имат ефект само в конфигурационния файл на сървъра, т.е. ако в конфигурационния файл на клиента има такъв таг, той бива игнориран. Конфигурирането на времената, свързани с жизнения цикъл, става чрез атрибутите на тага:
-
leaseTime – времето "на заем" на всеки обект, свързан с даденото приложение. По подразбиране има стойност 5 минути.
-
sponsorshipTimeout – времето, което Lease мениджърът изчаква отговора на спонсора след като го уведоми че даден Lease е изтекъл. Ако спонсорът не отговори в този период обектът подлежи на освобождаване от системата за събиране на боклука. По подразбиране времето е 2 минути.
-
renewOnCallTime – времето, с което се увеличава продължителността на живота на обект, при всяко негово извикване. За повече подробности относно начина на изчисляване на допълнителното време вижте частта "Интерфейсът ILease". Подразбиращата се стойност е 2 минути.
-
leaseManagerPollTime – времето, което Lease мениджърът изчаква, след като е проверил системата за Lease обекти с изтекло време, преди да започне нова проверка. По подразбиране периодът е 10 секунди.
Единиците за измерване на времето се записва веднага след стойността за всеки атрибут. Символите за мерните единици не са зависими от главни и малки букви. Валидните стойности са:
-
D – дни
-
H - часове
-
M - минути
-
S - секунди
-
MS – милисекунди
Ето как изглежда секцията в един примерен конфигурационен файл:
leaseTime="3m"
sponsorshipTimeout="30s"
renewOnCallTime="1m"
leaseManagerPollTime="750ms" />
...
|
Представлява под-елемент на . Не е задължителен елемент и може да се среща повече от веднъж в рамките на един конфигурационен файл. Използва се като контейнер, в който са изброени и описани всички отдалечени типове, които сървърът предоставя за използване. Поради това има смисъл само в приложения, които играят ролята на сървър. Тъй като дадено приложение може да бъде едновременно както клиент така и сървър, този таг е съвместим с тага . В този таг се съдържат таговете и , които са разгледани по-долу.
Представлява под-елемент на . Не е задължителен елемент и може да се среща повече от веднъж. Служи за контейнер, в който са изброени и описани типовете на отдалечените обекти, които приложението може да използва. Може да съдържа таговете и . Има следните атрибути:
-
url – определя URL адреса, който се използва за активиране на клиентски обекти. Ако приложението използва такъв тип обекти, този атрибут е задължителен.
-
displayName – използва се само от приложението Admin Tool, за да може потребителите му да отличават визуално различните такива тагове, когато са използвани повече от един. Този атрибут е незадължителен.
Може да се използва като под-елемент само на таговете и . И в двата случая този таг е незадължителен и може да се среща повече от веднъж. Тъй като между начините, по които се използва в двата случая, има различия, ще ги разгледаме поотделно.
В конфигурационния файл на сървъра този таг се намира под тага . Той описва отдалечените типове със сървърна активация, предлагани от сървъра. Чрез него се описват SingleCall и Singleton обектите. Има следните задължителни атрибути:
-
mode – типа на активирания на сървъра обект. Валидните стойности са SingleCall и Singleton.
-
type – означава пълния тип на обекта. Има следния формат:
<пълен тип на обекта>, <асембли, в което се намира типът>, Version=<версия>, PublicKeyToken=<публичен ключ на силно именуваното асембли>, Culture=<култура>
|
Version, PublicKeyToken и Culture се използват само при силно именувани асемблита.
-
objectUri – URI адрес на отдалечения обект, към който клиентът се обръща. Трябва да бъде уникално за всеки тип в приложението. Зависим е от малки и главни букви. Особеност при този атрибут е, че когато обектът се достъпва през IIS (Internet Information Services), e нужно той да завърша с разширение .soap. Повече за използването на Remoting чрез IIS можете да намерите в частта "Remoting сценарии".
В конфигурационния файл на клиента описва типовете със сървърна активация, които клиентът използва. Атрибутите, които се използват в този случай са:
-
type – означава пълния тип на обекта. Има същия формат като случая със сървърната конфигурация.
-
url – адресът, който трябва да се използва, за да може клиентът да се свърже със сървъра и неговите типове. Съдържанието на този адрес зависи и от това дали в тага е въведено име на приложението.
За да представим нагледно конфигурационните възможности нека разгледаме следващите два примера. Конфигурация на Remoting сървър:
server.config
|
type="CommonTypes.Query, CommonTypes"
objectUri="Query" />
type="CommonTypes.Library, CommonTypes"
objectUri="Library" />
|
Конфигурация на Remoting клиент:
client.config
|
url="http://remoting_server:1234/Query" />
url="http://remoting_server:1234/Library" />
|
Представлява под-елемент на таговете и съответно в сървърния и в клиентския конфигурационен файл. Описва типовете с клиентска активация, които приложението предлага или използва. Не е задължителен елемент и може да се среща многократно в тези две секции. Има един задължителен атрибут type, който се използва и в двата случая за описване на пълния тип на отдалечения обект. Ето пример за конфигуриране на клиентска активация от страна на сървъра:
Ето как изглежда съответната конфигурация от страна на клиента:
Представлява под-елемент на тага или .
Когато се намира под , с този таг се дефинират нови канали, създадени от разработчика.
В случая, когато той се намира под , конфигурираме вече съществуващи или вече описани канали в секцията под .
Стандартните канали като TCP и HTTP каналите са описани във файла machine.config. Ако искаме да променим тяхното поведение глобално, за цялата машина, можем да редактираме този файл, но това не е препоръчителна практика, тъй като може да отворим дупка в сигурността на машината. Друг проблем е, че ако нашето приложение изисква такива промени, то трудно би могло да се разпространява, тъй като трябва да се налага административна намеса в процеса на инсталиране, а също така е възможно да се получат несъвместимости с други приложения, които разчитат на стандартните настройки. Затова е по-добре да се използват локално дефинирани канали.
Може да се използва като под-елемент на без значение къде се намира родителската секция. С този таг се конфигурират параметрите на всеки един от каналите, които приложението използва. Може да се съдържа в една секция повече от веднъж. Поради това, че в зависимост от разположението на родителския таг, тази секция има различно значение, ще разгледаме двата случая по отделно.
Когато се намира под можем да разглеждаме тази секция като шаблон на канала. Тагът описва канала и някои от неговите свойства като например:
-
id – уникално име на канала, което се използва за рефериране на канала в други части на конфигурационния файл. Не трябва да се допускат канали с еднакви id атрибути, тъй като парсерът не предупреждава за грешка, а използва навсякъде последно декларирания канал с това id. Този атрибут е задължителен.
-
type – пълно име на класа, който имплементира канала. Описва се в същия формат, който се използва при атрибута type на тага . Задължителен атрибут.
-
name – име на канала. Използва се, когато се налага да се регистрира един и същ канал, който да "слуша" на повече от един порт.
-
priority – приоритет на канала спрямо другите регистрирани от приложението канали. При заявка към сървъра Remoting системата използва тази информация, за да намери подходящ канал за връзка. По-голям приоритет имат каналите с по-голяма стойност на този атрибут. Стандартните канали, които са част от .NET Framework, имат приоритет равен на 1. Отрицателните числа са също валидни стойности.
-
displayName – използва се само от приложението Admin Tool, за да може потребителите му да отличават визуално различните такива тагове, когато са използвани повече от един. Този атрибут е незадължителен.
-
Специфични за канала свойства. Тъй като свойствата на каналите не се контролират от Remoting инфраструктурата и всеки канал може да има специфични изисквания, чрез тази специална група от атрибути може да се подават специфичните стойности. За тях няма предефинирани атрибути. Форматът, в който се подават стойностите, е ключ-стойност. За по-нагледно представяне нека разгледаме един пример:
type="CommonTypes.Channels.CustomChannel, CommonTypes"
myProperty="myValue"
author="Viktor Zhivkov" />
|
Когато използваме в рамките на тага ние се обръщаме към вече дефиниран канал в друга секция или в machine.config файла. В този случай трябва да укажем следните задължителни атрибути:
-
ref – означава името (id атрибута) на шаблона, който реферираме.
-
port – номера на порта, на който каналът трябва да "слуша". В клиентските конфигурационни файлове можем да зададем стойност 0, при което Remoting системата автоматично избира вместо нас подходящия порт за връзка.
Каналите имат и други атрибути освен изброените задължителни. Ще разгледаме поотделно списъка с атрибути на стандартните вградени HTTP и TCP канали.
Атрибути на HTTP канал:
-
clientConnectionLimit – определя максималния брой на едновременните връзки за даден канал. По подразбиране има стойност 2.
-
proxyName – име на прокси сървър. По този начин се конфигурира Remoting инфраструктурата да използва прокси сървър.
-
proxyPort – порт на прокси сървъра, който да се използва за комуникация. Употребява се заедно с proxyName атрибута.
-
useIpAddress – булева стойност за това дали в URL адресите на предоставяните типове се използва IP адрес (true) или име на машина (false). Приложим е само в конфигурацията на сървъра. По подразбиране има стойност true.
-
machineName – име на машината, което да се използва при комуникация вместо истинското й име. Ако е подадена стойност автоматично атрибутът useIpAddress приема стойност false.
Атрибути на TCP канал:
-
useIpAddress – булева стойност за това дали в URL адресите на предоставяните типове се използва IP адрес (true) или име на машина (false). Приложим е само в конфигурацията на сървъра. По подразбиране има стойност true.
-
rejectRemoteRequests – булева стойност, която задава дали да се отхвърлят връзки от други машини. Когато има стойност true са разрешени само извиквания между процесите на една машина.
Намира се под тага и служи за контейнер на таговете, описващи тръбите на канала (channel sink). Тази секция от конфигурационния файл не е задължителна и може да се среща най-много веднъж във файл. Валидните под-елементи на този таг са и
Този елемент може да бъде използван както под тага , така и под . Той служи за дефиниране и конфигуриране на доставчиците (providers) от страна на сървъра.
По аналогия с каналите първата употреба дефинира нов sink provider, а втората конфигурира вече съществуващ. Стандартните доставчици са дефинирани във файла machine.config. Този таг може да се употребява само веднъж.
Стандартните доставчици и форматери на канала се преконфигурират, ако използваме този таг за рефериране или деклариране в секцията. В този случай трябва да изброим всички доставчици и форматери, които ще бъдат използвани от канала. Например, ако добавим в конфигурационния файл на клиента доставчик, а след това форматер, ще получим изключение (exception), ако доставчикът не имплементира нужен на форматера интерфейс.
В тази секция могат да се използват таговете и
.
Тагът е аналогичен на , с тази разлика, че дефинира и конфигурира доставчиците от страна на клиента.
Използва се в секциите и . Описва sink provider на канала, за който се отнася. Може да се среща нула или повече пъти в една секция. За конфигуриране се използват следните задължителни атрибути:
-
id – уникално име на доставчика, което ще се използва за рефериране.
-
type – пълен тип на класа, чиято инстанция ще бъде доставчика. Форматът на този атрибут е същият, както на досега разглежданите атрибути .
-
ref – посочва id на доставчика, който се реферира. Не може да се използва в секции, които дефинират такива.
Освен тези атрибути можем да подадем на конструктора на доставчика параметри чрез съдържанието на
тага. От гледна точка на Remoting системата, няма значение какво е името на тага вътре, тъй като той се подава като DOM структура. Всички XML атрибути и XML структури в този таг се подават на конструктора на доставчика, описан в конфигурацията. Всеки един от доставчиците трябва да има конструктори, които приемат IDictionary или ICollection като входни параметри. Речниковата колекция се използва за контейнер на подадените атрибути, а колекцията – за DOM структурата на елемента и неговите под-елементи.
Ето така изглежда част от един примерен конфигурационен файл. Тагът , както и атрибутите mode, mask, ip ще бъдат използвани като параметри за инстанцирането на доставчика от тип IpFilter (този тип е измислен за примера, не го търсете в .NET Framework!).
Използва се като под-елемент на и . Конфигурира какъв форматер ще използва даденият канал. Ако бъде пропуснат, се използва подразбиращият се форматер. Може да се изброят няколко форматера, които да обработват последователно данните, преминаващи през канала. В този случай другата страна в комуникацията трябва да бъде конфигурирана със същата последователност от форматери. За конфигуриране на форматера се използват следните атрибути:
-
id – уникално име на форматера, което ще се използва при рефериране.
-
type – пълен тип на класа, чиято инстанция ще бъде форматер. Форматът на този атрибут е същия, както в досега разглежданите атрибути .
-
ref – посочва id на форматера, който се реферира. Не може да се използва в секции, които дефинират такива.
-
typeFilterLevel – определя нивото на позволените извиквания. Има две стойности: Low и Full. Подразбиращата се стойност Low не позволява обръщения от тип callback. В случай, че ни се налага да използваме такива, трябва да променим тази стойност на Full. Най-честата причина за промяна на тази настройка на форматера е използването на спонсори с маршализация по референция (те имат нужда от callback извиквания).
Подобно на доставчиците, и форматерите могат да бъдат фино конфигурирани с потребителски атрибути и тагове в секцията. Процесът е аналогичен на разгледания по-горе.
Два реални конфигурационни файла – пример
За да обобщим и придадем форма на натрупаните до тук факти за конфигурационните файлове, нека разгледаме два цялостни реални примера:
Пример за цялостен конфигурационен файл за Remoting инфраструктурата от страна на сървъра:
server.config
|
type="CommonTypes.Query, CommonTypes"
objectUri="Query" />
type="CommonTypes.Library, CommonTypes"
objectUri="Library" />
|
Пример за цялостен конфигурационен файл за Remoting инфраструктурата от страна на клиента:
client.config
|
leaseTime="3m"
sponsorshipTimeout="30s"
renewOnCallTime="1m"
leaseManagerPollTime="750ms" />
url="http://remoting_server/RemotingApp/Query" />
url="http://remoting_server/RemotingApp/Library"/>
|
В тези два файла са показани най-важните и често срещани елементи. В случай, че използвате само вградените канали, доставчици и форматери, ще са ви достатъчни само такива конфигурационни файлове.
Сподели с приятели: |