Поделиться через


Инициализация создания экземпляров

Этот образец расширяет образец Объединение в пул с помощью определения интерфейса IObjectControl, который изменяет процесс инициализации объекта путем его активации или деактивации. Клиент вызывает методы, которые возвращают объект в пул и не возвращают объект в пул.

ms751409.note(ru-ru,VS.100).gifПримечание
Процедура настройки и инструкции по построению для данного образца приведены в конце этого раздела.

Точки расширяемости

При создании расширения Windows Communication Foundation (WCF) в первую очередь необходимо определить, какие точки расширяемости будут использоваться. В WCF термин EndpointDispatcher относится к компоненту среды выполнения, который отвечает за преобразование входящих сообщений в вызовы метода в службе пользователя и для преобразования возвращаемых этим методом значений в исходящие сообщения. Служба WCF создает для каждой конечной точки объект EndpointDispatcher.

EndpointDispatcher реализует расширяемость области конечной точки (для всех сообщений, получаемых и отправляемых службой) с помощью класса EndpointDispatcher. Этот класс позволяет настраивать различные свойства, управляющие поведением EndpointDispatcher. В этом образце рассматривается свойство InstanceProvider, которое указывает на объект, предоставляющий экземпляры класса службы.

IInstanceProvider

В WCF EndpointDispatcher создает экземпляры класса службы с помощью поставщика экземпляров, реализующего интерфейс IInstanceProvider. У этого интерфейса есть только два метода.

  • GetInstance. Когда прибывает сообщение, объект Dispatcher вызывает метод GetInstance, чтобы создать экземпляр класса службы для обработки сообщения. Частота вызовов этого метода определяется свойством InstanceContextMode. Например, если свойство InstanceContextMode имеет значение System.ServiceModel.InstanceContextMode.PerCall, для обработки каждого получаемого сообщения создается новый экземпляр класса службы, поэтому метод GetInstance вызывается каждый раз, когда приходит сообщение.

  • ReleaseInstance. Когда экземпляр службы завершает обработку сообщения, EndpointDispatcher вызывает метод ReleaseInstance. Как и в случае метода GetInstance, частота вызовов этого метода определяется свойством InstanceContextMode.

Пул объектов

Класс ObjectPoolInstanceProvider содержит реализацию пула объектов. Этот класс реализует интерфейс IInstanceProvider для взаимодействия с уровнем модели службы. Когда EndpointDispatcher вызывает метод GetInstance, вместо создания нового экземпляра, пользовательская реализация находит существующий объект в находящемся в памяти пуле. Если такой объект доступен, метод возвращает его. В противном случае ObjectPoolInstanceProvider проверяет, не достигло ли свойство ActiveObjectsCount (количество возвращенных из пула объектов) максимального размера пула. Если нет, то создается и возвращается вызывающей стороне новый экземпляр, а значение ActiveObjectsCount увеличивается на единицу. В противном случае запрос на создание объекта помещается в очередь на заданное время. Реализация метода GetObjectFromThePool показана в следующем образце кода.

private object GetObjectFromThePool()
{
    bool didNotTimeout = 
       availableCount.WaitOne(creationTimeout, true);
    if(didNotTimeout)
    {
         object obj = null;
         lock (poolLock)
        {
             if (pool.Count != 0)
             {
                   obj = pool.Pop();
                   activeObjectsCount++;
             }
             else if (pool.Count == 0)
             {
                   if (activeObjectsCount < maxPoolSize)
                   {
                        obj = CreateNewPoolObject();
                        activeObjectsCount++;
                            
                        #if (DEBUG)
                        WritePoolMessage(
                             ResourceHelper.GetString("MsgNewObject"));
                       #endif
                   }                        
            }
           idleTimer.Stop();
      }
     // Call the Activate method if possible.
    if (obj is IObjectControl)
   {
         ((IObjectControl)obj).Activate();
   }
   return obj;
}
throw new TimeoutException(
ResourceHelper.GetString("ExObjectCreationTimeout"));
}

Пользовательская реализация ReleaseInstance добавляет освободившийся экземпляр обратно в пул и уменьшает значение ActiveObjectsCount на единицу. EndpointDispatcher может вызывать эти методы из различных потоков, поэтому требуется синхронизированный доступ к членам уровня класса в классе ObjectPoolInstanceProvider.

        public void ReleaseInstance(InstanceContext instanceContext, object instance)
        {
            lock (poolLock)
            {
                // Check whether the object can be pooled. 
                // Call the Deactivate method if possible.
                if (instance is IObjectControl)
                {
                    IObjectControl objectControl = (IObjectControl)instance;
                    objectControl.Deactivate();

                    if (objectControl.CanBePooled)
                    {
                        pool.Push(instance);

                        #if(DEBUG)
                        WritePoolMessage(
                            ResourceHelper.GetString("MsgObjectPooled"));
                        #endif                        
                    }
                    else
                    {
                        #if(DEBUG)
                        WritePoolMessage(
                            ResourceHelper.GetString("MsgObjectWasNotPooled"));
                        #endif
                    }
                }
                else
                {
                    pool.Push(instance);

                    #if(DEBUG)
                    WritePoolMessage(
                        ResourceHelper.GetString("MsgObjectPooled"));
                    #endif 
                }
                                
                activeObjectsCount--;

                if (activeObjectsCount == 0)
                {
                    idleTimer.Start();                     
                }
            }

            availableCount.Release(1);
        }

Метод ReleaseInstance предоставляет функцию инициализации с очисткой. Обычно пул поддерживает минимальное число объектов в течение времени существования пула. Однако возможны периоды интенсивного использования, когда требуется создавать в пуле дополнительные объекты, пока не будет достигнуто заданное в конфигурации максимальное значение. В конце концов, когда активность пула снизится, эти дополнительные объекты могут стать излишней нагрузкой. Поэтому когда значение activeObjectsCount достигает нуля, запускается таймер бездействия, по истечении времени ожидания которого выполняется цикл очистки.

if (activeObjectsCount == 0)
{
    idleTimer.Start(); 
}

Расширения уровня ServiceModel выполняются с помощью следующих поведений.

  • Поведения служб. Позволяют настраивать всю среду выполнения службы.

  • Поведения конечных точек. Позволяют настраивать отдельные конечные точки, включая EndpointDispatcher.

  • Поведения контрактов. Позволяют настраивать классы ClientRuntime или DispatchRuntime на стороне клиента или службы соответственно.

  • Поведения операций. Позволяют настраивать классы ClientOperation или DispatchOperation на стороне клиента или службы соответственно.

С целью реализации расширения создания пулов объектов может быть создано поведение конечной точки или поведение службы. В этом примере используется поведение службы, которое применяет поддержку создания пулов объектов ко всем конечным точкам службы. Поведения служб создаются путем реализации интерфейса IServiceBehavior. Имеется несколько способов сообщить ServiceModel о пользовательских поведениях:

  • с помощью пользовательского атрибута;

  • путем ее императивного добавления в коллекцию поведений описания службы;

  • путем расширения файла конфигурации.

В этом образце используется пользовательский атрибут. При создании ServiceHost проверяются атрибуты, используемые в определении типа службы, а в коллекцию поведений описания службы добавляются доступные поведения.

У интерфейса IServiceBehavior имеется три метода: Validate, AddBindingParameters, и ApplyDispatchBehavior. Эти методы вызываются средой WCF при инициализации ServiceHost. Сначала вызывается метод System.ServiceModel.Description.IServiceBehavior.Validate(System.ServiceModel.Description.ServiceDescription,System.ServiceModel.ServiceHostBase); он позволяет проверить согласованность службы. Затем вызывается метод System.ServiceModel.Description.IServiceBehavior.AddBindingParameters(System.ServiceModel.Description.ServiceDescription,System.ServiceModel.ServiceHostBase,System.Collections.ObjectModel.Collection{System.ServiceModel.Description.ServiceEndpoint},System.ServiceModel.Channels.BindingParameterCollection); он используется только в очень сложных сценариях. Наконец вызывается метод System.ServiceModel.Description.IServiceBehavior.ApplyDispatchBehavior(System.ServiceModel.Description.ServiceDescription,System.ServiceModel.ServiceHostBase), который отвечает за настройку среды выполнения. Следующие параметры передаются методу System.ServiceModel.Description.IServiceBehavior.ApplyDispatchBehavior(System.ServiceModel.Description.ServiceDescription,System.ServiceModel.ServiceHostBase).

  • Description: этот параметр содержит описание службы для всей службы. Его можно использовать для проверки данных описания о конечных точках, контрактах и привязках службы, а также других связанных со службой данных.

  • ServiceHostBase: этот параметр содержит инициализируемый в данный момент объект ServiceHostBase.

В пользовательской реализации IServiceBehavior создается новый экземпляр ObjectPoolInstanceProvider, который присваивается свойству InstanceProvider в каждом объекте EndpointDispatcher, прикрепленном к ServiceHostBase.

public void ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase serviceHostBase)
{
    if (enabled)
    {
        // Create an instance of the ObjectPoolInstanceProvider.
        instanceProvider = new ObjectPoolInstanceProvider(description.ServiceType,
        maxPoolSize, minPoolSize, creationTimeout);

        // Assign our instance provider to Dispatch behavior in each 
        // endpoint.
        foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
        {
             ChannelDispatcher cd = cdb as ChannelDispatcher;
             if (cd != null)
             {
                 foreach (EndpointDispatcher ed in cd.Endpoints)
                 {
                        ed.DispatchRuntime.InstanceProvider = instanceProvider;
                 }
             }
         }
     }
} 

Помимо реализации интерфейса IServiceBehavior у класса ObjectPoolingAttribute имеется несколько членов для настройки пула объектов с помощью аргументов атрибута. К этим членам относятся MaxSize, MinSize, Enabled и CreationTimeout, и они должны соответствовать набору функций пула, предоставляемому службами .NET Enterprise Services.

Теперь в службу WCF можно добавить поведение пула объектов, создав заметку для реализации службы с новым пользовательским атрибутом ObjectPooling.

[ObjectPooling(MaxSize=1024, MinSize=10, CreationTimeout=30000]    
public class PoolService : IPoolService
{
  // …
}

Активация и деактивация связывания

Основной целью создания пулов объектов является оптимизация использования объектов с небольшим временем существования, чтобы экономить на ресурсоемких процедурах создания и инициализации. Поэтому создание пулов при его правильном использовании позволяет значительно повысить производительность приложения. Поскольку объект возвращается из пула, конструктор вызывается только один раз. Однако некоторым приложениями требуется более высокий уровень контроля, чтобы они могли инициализировать и высвобождать ресурсы в рамках одного контекста. Например, объект, используемый для набора вычислений, может сбрасывать значения своих закрытых полей, прежде чем переходить к следующим вычислениям. Службы Enterprise Services поддерживают такую инициализацию в зависимости от контекста, позволяя разработчику объектов переопределять методы Activate и Deactivate из базового класса ServicedComponent.

Пул объектов вызывает метод Activate непосредственно перед возвращением объекта из пула. Метод Deactivate вызывается, когда объект возвращается в пул. Кроме того, у базового класса ServicedComponent имеется свойство типа boolean с именем CanBePooled, с помощью которого можно уведомить пул о том, можно ли и дальше размещать объект в пуле.

Чтобы сымитировать эту функциональность, в этом образце объявляется открытый интерфейс (IObjectControl), имеющий указанные выше члены. Затем этот интерфейс реализуется классами службы, предназначенными для инициализации с учетом контекста. Реализацию IInstanceProvider необходимо изменить, чтобы она удовлетворяла этим требованиям. Теперь при каждом получении объекта с помощью метода GetInstance необходимо проверять, является ли объект реализацией интерфейса IObjectControl. Если он не является такой реализацией, необходимо вызвать метод Activate.

if (obj is IObjectControl)
{
    ((IObjectControl)obj).Activate();
}

При возврате объекта в пул необходимо проверить свойство CanBePooled, прежде чем добавлять объект обратно в пул.

if (instance is IObjectControl)
{
    IObjectControl objectControl = (IObjectControl)instance;
    objectControl.Deactivate();
    if (objectControl.CanBePooled)
    {
       pool.Push(instance);
    }
}

Поскольку разработчик может решать, можно ли помещать объект в пул, в определенный момент значение счетчика объектов в пуле может оказаться меньше минимального размера пула. Поэтому необходимо сравнивать число объектов с минимальным размером и при необходимости выполнять инициализацию в процедуре очистки.

// Remove the surplus objects.
if (pool.Count > minPoolSize)
{
  // Clean the surplus objects.
}                    
else if (pool.Count < minPoolSize)
{
  // Reinitialize the missing objects.
  while(pool.Count != minPoolSize)
  {
    pool.Push(CreateNewPoolObject());
  }
}

При выполнении образца запросы и ответы операций отображаются в окнах консоли как службы, так и клиента. Нажмите клавишу ВВОД в каждом окне консоли, чтобы закрыть службу и клиент.

Настройка, построение и выполнение образца

  1. Убедитесь, что выполнены процедуры, описанные в разделе Процедура однократной настройки образцов Windows Communication Foundation.

  2. Чтобы выполнить построение решения, следуйте инструкциям раздела Построение образцов Windows Communication Foundation.

  3. Чтобы выполнить образец на одном или нескольких компьютерах, следуйте инструкциям в разделе Running the Windows Communication Foundation Samples.

ms751409.Important(ru-ru,VS.100).gif Примечание
Образцы уже могут быть установлены на компьютере. Перед продолжением проверьте следующий каталог (по умолчанию).

<диск_установки>:\WF_WCF_Samples

Если этот каталог не существует, перейдите на страницу Образцы Windows Communication Foundation (WCF) и Windows Workflow Foundation (WF) для .NET Framework 4, чтобы загрузить все образцы Windows Communication Foundation (WCF) и WF. Этот образец расположен в следующем каталоге.

<диск_установки>:\WF_WCF_Samples\WCF\Extensibility\Instancing\Initialization