Для иллюстрации особенностей лизинга по умолчанию для удаленных СAО-типов и WKO-синглетов определим в нашем текущем проекте CAOCarGeneralAsm новый внутренний класс LeaseInfo. Статический член LeaseStats() этого класса выводит информацию о текущем лизинге для типа CarProvider в окно консоли сервера (не забудьте указать директиву using для пространства имен System.Runtime.Remoting.Lifetime, чтобы сообщить компилятору о месте нахождения определении типа ILease).
internal class LeaseInfo {
public static void LeaseStats(ILease itfLease) {
Console.WriteLine(***** Информация о лизинге *****");
Console.WriteLine("Состояние лизинга: {0}", itfLease.CurrentState);
Console.WriteLine("Начальное время лизинга: {0}:{1}", itfLease.InitialLeaseTime.Minutes, itfLease.InitialLeaseTime.Seconds);
Console.WriteLine("Текущее время лизинга: {0}:{1}", itfLease.CurrentLeaseTime.Minutes, itfLease.CurrentLeaseTime.Seconds);
Console.WriteLine("Обновление времени при вызове: {0}:{1}", itfLease.RenewOnCallTime.Minutes, itfLease.RenewOnCallTime.Seconds);
Console.WriteLine();
}
}
Теперь предположим, что LeaseInfo.LeaseStats() вызывается в рамках методов GetJBCByIndex() и GetAllAutos() типа CarProvider. После перекомпиляции компоновочных блоков сервера и клиента (снова для гарантии того, что система Visual Studio 2005 скопирует самую последнюю и наиболее полную версию CarGeneralAsm.dll в каталоги приложений клиента и сервера), выполните приложение еще раз. Окно консоли вашего сервера должно теперь быть похожим на то, которое показано на рис. 18.6.
Рис. 18.6. Информация лизинга по умолчанию для CarProvider
Изменение параметров схемы лизингового управления
Очевидно, параметры лизинга по умолчанию не могут годиться во всех случаях и для всех удаленных САО-объектов и WKO-синглетов. Если вы хотите изменить типовые установки, у вас на выбор есть два варианта.
• Установки лизинга, принятые по умолчанию, можно изменить с помощью файла *.config сервера.
• Установки лизинга, принятые по умолчанию, можно изменить программными средствами путем переопределения членов базового класса MarshalByRefObject.
Каждый из этих подходов действительно позволяет изменить установки лизинга, принятые по умолчанию, но между этими подходами есть принципиальная разница. При использовании файла *.config сервера установки лизинга применяются ко всем объектам, размещаемым в рамках процесса сервера. При переопределении отдельных членов типа MarshalByRefObject появляется возможность изменять установки лизинга для каждого объекта в отдельности.
Чтобы продемонстрировать изменение параметров лизинга по умолчанию с помощью файла *.config, добавим к XML-данным сервера дополнительный элемент ‹lifetime›.
‹configuration›
‹system.runtime.remoting›
‹application›
‹lifetime leaseTime = "15M" renewOnCallTime = "5M"/›
‹service›
‹activated type="CarGeneralAsm.CarProvider, CarGeneralAsm" /›
‹/service›
‹channels›
‹channel ref="tcp" port="32469" /›
‹/channels›
‹/application
‹/system.runtime.remoting›
‹/configuration›
Обратите внимание на то, что в значениях свойств leaseTime и renewOnCallTime используется суффикс M, который, как вы можете догадаться сами, при установке времени для лизинга обозначает использование минут в качестве единицы измерения. При необходимости числовые значения элемента ‹lifetime› могут также содержать суффиксы MS (миллисекунды), S (секунды), Н (часы) и даже D (дни).
Повторим, что при изменении файла *.config сервера вы изменяете параметры лизинга для каждого САО-объекта и WKO-синглета в рамках сервера. Как альтернативу, можно использовать программное переопределение метода InitializeLifetime() конкретного удаленного типа.
public class CarProvider: MarshalByRefObject {
public override object InitializeLifetimeService() {
// Получение текущей информации лизинга.
ILease itfLeaseInfo = (ILease)base.InitializeLifetimeService();
// Изменение установок.
itfLeaseInfo.InitialLeaseTime = TimeSpan.FromMinutes(50);
itfLeaseInfo.RenewOnCallTime = TimeSpan.FromMinutes(10);
return itfLeaseInfo;
}
…
}
Здесь CarProvider устанавливает значение 50 минут для InitialLeaseTime и значение 10 – для RenewOnCallTime. Снова подчеркнем, что преимуществом переопределения метода InitializeLifetimeServices() является возможность, настройки каждого удаленного типа в отдельности.
Наконец, чтобы вообще отключить ограничения для времени лизинга данного СAО-типа или WKO-синглета, переопределите InitializeLifetimeServices() так, чтобы возвращалось значение null. В результате вы, по сути, укажете МВR-тип, который будет существовать всё время, пока будет работать хост-приложение сервера.
Настройка параметров лизинга на стороне сервера
Вы только видели, что переопределение метода InitializeLifetimeServices() MBR-типом позволяет изменить текущие параметры лизинга во время активизации типа. Но что делать, если удаленному типу нужно изменить параметры лизинга после активизации? Предположим, например, что тип CarProvider предлагает новый метод, выполняющий операцию, требующую много времени (например; соединение с удаленной базой данных с последующим чтением большого набора записей). Перед началом выполнения такого заданий вы можете программно изменить время лизинга так, чтобы в случае, когда остаток времени становится менее одной минуты, время лизинга снова увеличивалось до десяти минут. Для этого можно использовать наследуемые методы MarshalByRefObject.GetLifetimeService() и ILease.Renew() так, как предлагается ниже.
// Корректировка параметров лизинга на стороне сервера.
// Предполагается, что это новый метод типа CarProvider.
public void DoLengthyOperation() {
ILease itfLeaseInfo = (ILease)this.GetLifetimeService();
if (itfLeaseInfo.CurrentLeaseTime.TotalMinutes ‹ 1.0) itfLeaseInfo.Renew(TimeSpan.FromMiutes(10));
// Выполнение длительной операции…
}
Настройка параметров лизинга на стороне клиента
В дополнение к указанным возможностям ILease, домен приложения клиента тоже может регулировать текущие параметры лизинга CAO-типов и WKD-сингле-тов, с которыми осуществляется удаленное взаимодействие. Для этого клиент должен использовать статический метод RemotingServices.GetLifetimeService(). В качестве параметра указанному члену клиент должен передать ссылку на удаленный тип так, как показано ниже.
// Корректировка параметров лизинга на стороне клиента.
CarProvider ср = new CarProvider(сars);
ILease itfLeaseInfo = (ILease)RemotingServices.GetLifetimeServiсе(cp);
if (itfLeaseInfo.CurrentLeaseTime.TotalMinutes ‹ 10.0) itfLeaseInfo.Renew(TimeSpan.FromMinutes(1000));
Такой подход может быть полезен тогда, когда домен приложения клиента готов начать выполнение длительной операции в потоке, использующем удаленный тип. Например, если однопоточное приложение должно напечатать документ, содержащий 100 страниц текста, очень велика вероятность того, что удаленный САО-тип или WKO-синглет может выйти за рамки отведенного для процесса времени. Надеюсь, вы уловили общую идею, хотя здесь, конечно, более "элегантным" решением является создание нового потока выполнения.
Спонсоры лизинга сервера (и клиента)
Заключительной темой нашего связанного с лизингом обсуждения цикла существования САО-типов и WKO-синглетов будет спонсорство лизинга. Как вы только что убедились, для каждого объекта САО-типа и WKO-синглета имеются параметры лизинга, используемые по умолчанию, которые можно изменить несколькими способами, как на стороне сервера, так и на стороне клиента. Но, независимо от конфигурации лизинга типа, в конечном итоге время лизинга MBR-объекта истечет. В этот момент среда выполнения отправит данный объект в "мусорник"… ну, хорошо, почти отправит.
Суть в том, что перед тем, как пометить ненужный тип для отправки сборщику мусора, среда выполнения проверяет, не имеет ли данный MBR-объект зарегистрированных спонсоров лизинга. Простыми словами, спонсор – это тип, реализующий интерфейс ISponsor, который определен так, как показано ниже.
public interface System.Runtime.Remoting.Lifetime.ISponsor {
TimeSpan Renewal(ILease lease);
}
Если среда выполнения обнаружит, что у MBR-объекта имеется спонсор, этот объект не будет сразу же отправлен сборщику мусора, а будет вызван метод Renewal() объекта спонсора, чтобы (еще раз) добавить время к текущему времени лизинга. С другой стороны, если окажется, что для данного MBR-типа спонсора нет, цикл существования объекта действительно закончится.
Предположим, что вы создали пользовательский класс, реализующий ISponsor и вызывающий метод Renewal() для возврата конкретной величины времени (через тип TimeSpan). Тогда как ассоциировать указанный тип с данным удаленным объектом? И снова это может быть сделано либо доменом приложения сервера, либо доменом приложения клиента.