ЯЗЫК ПРОГРАММИРОВАНИЯ С# 2005 И ПЛАТФОРМА .NET 2.0. 3-е издание - Эндрю Троелсен
Шрифт:
Интервал:
Закладка:
Суть в том, что перед тем, как пометить ненужный тип для отправки сборщику мусора, среда выполнения проверяет, не имеет ли данный MBR-объект зарегистрированных спонсоров лизинга. Простыми словами, спонсор – это тип, реализующий интерфейс ISponsor, который определен так, как показано ниже.
public interface System.Runtime.Remoting.Lifetime.ISponsor {
TimeSpan Renewal(ILease lease);
}
Если среда выполнения обнаружит, что у MBR-объекта имеется спонсор, этот объект не будет сразу же отправлен сборщику мусора, а будет вызван метод Renewal() объекта спонсора, чтобы (еще раз) добавить время к текущему времени лизинга. С другой стороны, если окажется, что для данного MBR-типа спонсора нет, цикл существования объекта действительно закончится.
Предположим, что вы создали пользовательский класс, реализующий ISponsor и вызывающий метод Renewal() для возврата конкретной величины времени (через тип TimeSpan). Тогда как ассоциировать указанный тип с данным удаленным объектом? И снова это может быть сделано либо доменом приложения сервера, либо доменом приложения клиента.
Для этого заинтересованная сторона должна получить ссылку ILease (с помощью наследуемого метода GetLifetimeService() на стороне сервера или статического метода RemotingServices.GetLifetimeService() на стороне клиента) и вызвать Register().
// Регистрация спонсора на стороне сервера.
CarSponsor mySponsor = new CarSponsor();
ILease itfLeaseInfo = (ILease)this.GetLifetimeService();
itfLeaseInfo.Register(mySponsor);
// Регистрация спонсора на стороне клиента.
CarSponsor mySponsor = new CarSponsor();
CarProvider cp = new CarProvider(cars);
ILease itfLeaseInfo = (ILease)Remoting.Services.GetLifetimeService(cp);
itfLeaseInfo.Register.(mySponsor);
В любом случае, если клиент или сервер желают отменить спонсорство, это можно сделать с помощью метода ILease.Unregister(), например:
// Отключение спонсора для данного объекта.
itfLeaseInfo.Unregister(mySponsor);
Замечание. Объекты клиента, имеющие спонсоры, кроме необходимости реализации ISponsor должны быть производными от MarshalByRefObject, поскольку клиент должен передать спонсор в удаленный домен приложения.
Как видите, управление циклом существования MBR-типов, кумулятивно изменяющих параметры своего состояния в процессе выполнения вызовов клиентов, оказывается немного более сложным, чем простая сборка мусора. На стороне преимуществ мы имеем широкие возможности управления относительно того, когда именно следует уничтожить удаленный тип. С другой стороны, существует вероятность того, что удаленный тип будет уничтожен без ведома клиента. Если клиент попытается вызвать члены типа, уже удаленного из памяти, среда выполнения сгенерирует исключение System.Runtime.Remoting.RemotingException, и в этот момент клиент может либо создать новый экземпляр удаленного типа, либо выполнить другие предусмотренные для такого случая действия.
Исходный код. Проекты CAOCarGeneralAsmLease, CAOCarProviderServerLease и CAOCarProviderClientLease размещены в подкаталоге, соответствующем главе 18.
Альтернативные хосты для удаленных объектов
При изучении материала этой главы вы создали группу консольных серверных хостов, обеспечивающих доступ к некоторому множеству удаленных объектов. Если вы имеете опыт использования классической модели DCOM (Distributed Component Object Model – распределенная модель компонентных объектов), соответствующие шаги могут показаться вам немного странными. В мире DCOM обычно строится один COM-сервер (на стороне сервера), содержащий удаленные объекты, который несет ответственность и за прием запросов, поступающих от удаленного клиента. Это единственное DCOM-приложение *.exe "спокойно" загружается в фоновом режиме без создания, в общем-то ненужного командного окна.
При построении компоновочного блока сервера .NET велика вероятность того, что удаленной машине не придется отображать никаких сообщений. Скорее всего, вам понадобится сервер, который только откроет подходящие каналы и зарегистрирует удаленные объекты для доступа клиента. Кроме того, при наличии простого консольного хоста вам (или кому-нибудь другому) придется вручную запустить компоновочный блок *.exe на стороне сервера, поскольку система удаленного взаимодействия .NET не предусматривает автоматический запуск файла *.exe на стороне сервера при вызове удаленным клиентом.
С учетом этих проблем возникает следующий вопрос: как создать невидимый приемник, который загрузится автоматически? Программисты .NET предлагают здесь на выбор две возможности.
• Построение .NET-приложения сервиса Windows, готового предложить хостинг для удаленных объектов.
• Разрешение осуществлять хостинг для удаленных объектов серверу IIS (Internet Information Server – информационный сервер Интернет).
Хостинг удаленных объектов с помощью сервиса Windows
Возможно, идеальным хостом для удаленных объектов является сервис Windows, поскольку сервис Windows позволяет следующее.
• Может загружаться автоматически при запуске системы
• Может запускаться, как "невидимый" процесс в фоновом режиме
• Может выполняться от имени конкретной учетной записи пользователя
Создать пользовательский сервис Windows средствами .NET исключительно просто, особенно в сравнении с возможностями непосредственного использования Win32 API. Для примера мы создадим проект Windows Service с именем CarWinService (рис. 18.7), который будет осуществлять хостинг удаленных типов, содержащихся в CarGeneralAsm.dll.
В результате Visual Studio 2005 сгенерирует парциальный класс (названный по умолчанию Service1), полученный из System.ServiceProcess.ServiceBase, и еще один класс (Program), реализующий метод Main() сервиса. Поскольку Service1 нельзя считать достаточно информативным именем для пользовательского сервиса, с помощью окна свойств укажите для свойств (Name) и ServiceName значение CarService. Различие между этими двумя свойствами в том, что значение (Name) задает имя, используемое для обращения к типу в программном коде, а свойство ServiceName обозначает имя, отображаемое в окне конфигурации сервисов Windows.
Перед тем как двигаться дальше, установите ссылки на компоновочные блоки CarGeneralAsm.dll и System.Remoting.dll, а также укажите следующие строки директив using в файле, содержащем определение класса CarService.
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels.Http;
using System.Runtime.Remoting.Channels;
using System.Diagnostics;
Рис. 18.7. Создание рабочего пространства нового проекта Windows Service
Реализация метода Main()Метод Main() класса Program обеспечивает запуск сервисов, определённых в проекте, путём передачи массива типов ServiceBase статическому методу Service.Run(). При условии, что имя пользовательского сервиса было изменено с Service1 на CarService, вы должны иметь следующее определение класса (для ясности программного кода здесь удалены комментарии).
static class Program {
static void Main() {
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[] { new CarService() };
ServiceBase.Run(ServicesToRun);
}
}
Реализация метода CarService.OnStart()Вы, вероятно, уже догадываетесь, какая программная логика должна использоваться при запуске пользовательского сервиса. Напомним, что целью CarService является выполнение той задачи, которую выполнял ваш консольный сервис. Поэтому, чтобы зарегистрировать CarService в виде WKO-синглета, доступного по протоколу HTTP, можете добавить в метод OnStart() следующий программный код (при использовании сервисов Windows для обслуживания удаленных объектов вместо "жестко" запрограммированной реализации можно использовать тип RemotingConfiguration, позволяющий загрузить файл *.config удаленного взаимодействия на стороне сервера).
protected override void OnStart(string[] args) {
// Создание нового HttpChannel.
HttpChannel с = new HttpChannel(32469);
ChannelServices.RegisterChannel(c);
// Регистрация WKO-типа одиночного вызова.
RemotingConfiguration.RegisterWellKnownServiceType(typeof(CarGeneralAsm.CarProvider), "CarProvider.soap", WellKnownObjectMode.SingleCall);
// Сообщение об успешном старте.
EventLog.WriteEntry("CarWinService", "CarWinService стартовал успешно!", EventLogEntryType.Information);
}
Заметим, что после регистрации типа в журнал регистрации событий Windows (с помощью типа System.Diagnostics.EventLog) записывается пользовательское сообщение с информацией о том, что машина-хост успешно запустила ваш сервис.
Реализация метода OnStop()Строго говоря, CarService не требует никакой программной логики остановки. Но для примера давайте направим в EventLog еще одно событие, на этот раз с информацией о завершении работы пользовательского сервиса Windows.
protected override void OnStop() {
EventLog.WriteEntry("CarWinService", "CarWinService остановлен", EventLogEntryType.Information);
}
Теперь, когда сервис полностью оформлен, следующей задачей является установка этого сервиса на удаленной машине.