Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В примере пулирования показано, как расширить Windows Communication Foundation (WCF) для поддержки пула объектов. В примере показано, как создать атрибут, который синтаксически и семантично похож на ObjectPoolingAttribute функциональные возможности атрибутов корпоративных служб. Пул объектов может повысить производительность приложения. Однако это может иметь противоположный эффект, если он не используется должным образом. Объединение объектов помогает сократить затраты на повторное создание часто используемых объектов, требующих обширной инициализации. Тем не менее, если вызов метода на объекте из пула занимает значительное время, пул объектов начинает ставить дополнительные запросы в очередь, как только достигается его максимальный размер. Таким образом, он может не обслуживать некоторые запросы на создание объекта, вызывая исключение времени ожидания.
Замечание
Процедура установки и инструкции по сборке для этого примера находятся в конце этого раздела.
Первым шагом при создании расширения WCF является решение точки расширяемости, используемой.
В WCF термин диспетчер обозначает компонент среды выполнения, который отвечает за преобразование входящих сообщений в вызовы методов пользовательской службы и преобразование возвращаемых значений из этого метода в исходящее сообщение. Служба WCF создает диспетчер для каждой конечной точки. Клиент WCF должен использовать диспетчер, если контракт, связанный с этим клиентом, является дуплексным.
Диспетчеры каналов и конечных точек обеспечивают расширяемость на уровне канала и контракта, раскрывая различные свойства, которые контролируют поведение диспетчера. Это DispatchRuntime свойство также позволяет проверять, изменять или настраивать процесс отправки. В этом образце рассматривается свойство InstanceProvider, которое указывает на объект, предоставляющий экземпляры класса службы.
The IInstanceProvider
В WCF диспетчер создает экземпляры класса службы с помощью объекта InstanceProvider, который реализует интерфейс IInstanceProvider. Этот интерфейс имеет три метода:
GetInstance(InstanceContext, Message): при поступлении сообщения диспетчер вызывает GetInstance(InstanceContext, Message) метод для создания экземпляра класса службы для обработки сообщения. Частота вызовов этого метода определяется свойством InstanceContextMode. Например, если свойство InstanceContextMode установлено так, чтобы создавать новый экземпляр класса PerCall службы, создается новый экземпляр класса PerCall для обработки каждого поступившего сообщения, поэтому вызывается при каждом поступлении сообщения.
GetInstance(InstanceContext): это идентично предыдущему методу, за исключением того, что он вызывается, если аргумент Message отсутствует.
ReleaseInstance(InstanceContext, Object): когда время существования экземпляра службы истекло, диспетчер вызывает ReleaseInstance(InstanceContext, Object) метод. Как и для GetInstance(InstanceContext, Message) метода, частота вызовов этого метода определяется свойством InstanceContextMode .
Пул объектов
Пользовательская IInstanceProvider реализация предоставляет необходимую семантику пула объектов для службы. Таким образом, этот пример имеет тип, предоставляющий настраиваемую ObjectPoolingInstanceProvider реализацию IInstanceProvider для пула.
Dispatcher При вызове GetInstance(InstanceContext, Message) метода вместо создания нового экземпляра пользовательская реализация ищет существующий объект в пуле памяти. Если такой объект доступен, метод возвращает его. В противном случае создается новый объект. Реализация метода GetInstance показана в следующем образце кода.
object IInstanceProvider.GetInstance(InstanceContext instanceContext, Message message)
{
object obj = null;
lock (poolLock)
{
if (pool.Count > 0)
{
obj = pool.Pop();
}
else
{
obj = CreateNewPoolObject();
}
activeObjectsCount++;
}
WritePoolMessage(ResourceHelper.GetString("MsgNewObject"));
idleTimer.Stop();
return obj;
}
Пользовательская реализация ReleaseInstance добавляет освободившийся экземпляр обратно в пул и уменьшает значение ActiveObjectsCount на единицу. Методы Dispatcher могут вызываться из разных потоков, поэтому требуется синхронизированный доступ к членам уровня класса в ObjectPoolingInstanceProvider классе.
void IInstanceProvider.ReleaseInstance(InstanceContext instanceContext, object instance)
{
lock (poolLock)
{
pool.Push(instance);
activeObjectsCount--;
WritePoolMessage(
ResourceHelper.GetString("MsgObjectPooled"));
// When the service goes completely idle (no requests
// are being processed), the idle timer is started
if (activeObjectsCount == 0)
idleTimer.Start();
}
}
Этот ReleaseInstance метод предоставляет функцию для очистки при инициализации. Обычно пул поддерживает минимальное число объектов в течение времени существования пула. Однако возможны периоды интенсивного использования, когда требуется создавать в пуле дополнительные объекты, пока не будет достигнуто заданное в конфигурации максимальное значение. В конечном итоге, когда пул становится менее активным, эти избыточные объекты могут стать дополнительными издержками. Поэтому, когда activeObjectsCount достигает нуля, запускается таймер простоя, запуск повлечёт активацию и выполнение цикла очистки.
Добавление поведения
Расширения уровня диспетчера подключены с использованием следующих процедур:
Поведение службы. Они позволяют настраивать всю среду выполнения службы.
Поведение конечных точек. Они позволяют настраивать конечные точки службы, в частности диспетчер каналов и конечных точек.
Поведение контракта. Они позволяют настраивать оба ClientRuntimeDispatchRuntime класса в клиенте и службе соответственно.
Для реализации расширения функционала пула объектов необходимо создать поведение службы. Поведения служб создаются путем реализации интерфейса IServiceBehavior. Существует несколько способов сделать модель службы осведомленной о пользовательском поведении.
с помощью пользовательского атрибута;
Императивно добавляя его в коллекцию поведений описания службы.
путем расширения файла конфигурации.
В этом образце используется пользовательский атрибут. При создании ServiceHost проверяются атрибуты, которые используются в определении типа службы, и добавляются доступные поведения в коллекцию поведений описания службы.
IServiceBehavior Интерфейс содержит три метода в нем : Validate, AddBindingParametersи ApplyDispatchBehavior. Метод Validate используется для обеспечения применения поведения к службе. В этом примере реализация гарантирует, что служба не настроена Single. Метод AddBindingParameters используется для настройки привязок службы. В этом сценарии не требуется. ApplyDispatchBehavior используется для настройки диспетчеров службы. Этот метод вызывается WCF при инициализации ServiceHost. Следующие параметры передаются в этот метод:
Description: этот аргумент предоставляет описание услуги для всего сервиса. Это можно использовать для проверки данных описания конечных точек службы, контрактов, привязок и других данных.ServiceHostBase: этот аргумент предоставляет ServiceHostBase, который в настоящее время инициализируется.
В пользовательской IServiceBehavior реализации создается новый экземпляр ObjectPoolingInstanceProvider, который назначается свойству InstanceProvider в каждом экземпляре DispatchRuntime в ServiceHostBase.
void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase serviceHostBase)
{
// Create an instance of the ObjectPoolInstanceProvider.
ObjectPoolingInstanceProvider instanceProvider = new
ObjectPoolingInstanceProvider(description.ServiceType,
minPoolSize);
// Forward the call if we created a ServiceThrottlingBehavior.
if (this.throttlingBehavior != null)
{
((IServiceBehavior)this.throttlingBehavior).ApplyDispatchBehavior(description, serviceHostBase);
}
// In case there was already a ServiceThrottlingBehavior
// (this.throttlingBehavior==null), it should have initialized
// a single ServiceThrottle on all ChannelDispatchers.
// As we loop through the ChannelDispatchers, we verify that
// and modify the ServiceThrottle to guard MaxPoolSize.
ServiceThrottle throttle = null;
foreach (ChannelDispatcherBase cdb in
serviceHostBase.ChannelDispatchers)
{
ChannelDispatcher cd = cdb as ChannelDispatcher;
if (cd != null)
{
// Make sure there is exactly one throttle used by all
// endpoints. If there were others, we could not enforce
// MaxPoolSize.
if ((this.throttlingBehavior == null) &&
(this.maxPoolSize != Int32.MaxValue))
{
throttle ??= cd.ServiceThrottle;
if (cd.ServiceThrottle == null)
{
throw new
InvalidOperationException(ResourceHelper.GetString("ExNullThrottle"));
}
if (throttle != cd.ServiceThrottle)
{
throw new InvalidOperationException(ResourceHelper.GetString("ExDifferentThrottle"));
}
}
foreach (EndpointDispatcher ed in cd.Endpoints)
{
// Assign it to DispatchBehavior in each endpoint.
ed.DispatchRuntime.InstanceProvider =
instanceProvider;
}
}
}
// Set the MaxConcurrentInstances to limit the number of items
// that will ever be requested from the pool.
if ((throttle != null) && (throttle.MaxConcurrentInstances >
this.maxPoolSize))
{
throttle.MaxConcurrentInstances = this.maxPoolSize;
}
}
Помимо реализации интерфейса IServiceBehavior у класса ObjectPoolingAttribute имеется несколько членов для настройки пула объектов с помощью аргументов атрибута. К этим элементам относятся MaxPoolSize, MinPoolSizeи CreationTimeoutдля сопоставления набора функций пула объектов, предоставляемых службами .NET Enterprise Services.
Теперь поведение пула объектов можно добавить в службу WCF, заметив реализацию службы с новым пользовательским ObjectPooling атрибутом.
[ObjectPooling(MaxPoolSize=1024, MinPoolSize=10, CreationTimeout=30000)]
public class PoolService : IPoolService
{
// …
}
Запуск тестового примера
В примере показаны преимущества производительности, которые можно получить с помощью пула объектов в определенных сценариях.
Приложение-служба реализует две службы: WorkService и ObjectPooledWorkService. Оба сервиса используют одну и ту же реализацию — они оба требуют затратной инициализации, а затем предоставляют метод DoWork(), который относительно недорог. Единственное различие заключается в том, что для ObjectPooledWorkService настроен пул объектов.
[ObjectPooling(MinPoolSize = 0, MaxPoolSize = 5)]
public class ObjectPooledWorkService : IDoWork
{
public ObjectPooledWorkService()
{
Thread.Sleep(5000);
ColorConsole.WriteLine(ConsoleColor.Blue, "ObjectPooledWorkService instance created.");
}
public void DoWork()
{
ColorConsole.WriteLine(ConsoleColor.Blue, "ObjectPooledWorkService.GetData() completed.");
}
}
При запуске клиента измеряется время выполнения вызова WorkService 5 раз. Затем измеряется время на вызов ObjectPooledWorkService 5 раз. Затем отображается разница во времени:
Press <ENTER> to start the client.
Calling WorkService:
1 - DoWork() Done
2 - DoWork() Done
3 - DoWork() Done
4 - DoWork() Done
5 - DoWork() Done
Calling WorkService took: 26722 ms.
Calling ObjectPooledWorkService:
1 - DoWork() Done
2 - DoWork() Done
3 - DoWork() Done
4 - DoWork() Done
5 - DoWork() Done
Calling ObjectPooledWorkService took: 5323 ms.
Press <ENTER> to exit.
Замечание
При первом запуске клиента обе службы, как представляется, занимают примерно одно и то же время. При повторном запуске примера можно заметить, что ObjectPooledWorkService возвращается гораздо быстрее, так как экземпляр этого объекта уже находится в пуле.
Настройка, сборка и запуск примера
Убедитесь, что вы выполнили процедуру настройки One-Time для образцов Windows Communication Foundation.
Чтобы создать решение, следуйте инструкциям по созданию примеров Windows Communication Foundation.
Чтобы запустить пример в конфигурации с одним или несколькими компьютерами, следуйте инструкциям в запуска примеров Windows Communication Foundation.
Замечание
Если вы используете Svcutil.exe для повторного создания конфигурации для этого примера, обязательно измените имя конечной точки в конфигурации клиента, чтобы соответствовать коду клиента.