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


Пользовательский срок службы

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

Замечание

Процедура настройки и инструкции по построению для данного примера приведены в конце этого раздела.

Общие входящие в систему

WCF предлагает несколько режимов создания экземпляров для ваших экземпляров служб. Общий режим инстантирования, описанный в этой статье, предоставляет способ совместного использования экземпляра службы между несколькими каналами. Клиенты могут обратиться к фабричному методу в службе и создать новый канал для начала связи. В следующем фрагменте кода показано, как клиентское приложение создает новый канал для существующего экземпляра службы:

// Create a header for the shared instance id
MessageHeader shareableInstanceContextHeader = MessageHeader.CreateHeader(
        CustomHeader.HeaderName,
        CustomHeader.HeaderNamespace,
        Guid.NewGuid().ToString());

// Create the channel factory
ChannelFactory<IEchoService> channelFactory =
    new ChannelFactory<IEchoService>("echoservice");

// Create the first channel
IEchoService proxy = channelFactory.CreateChannel();

// Call an operation to create shared service instance
using (new OperationContextScope((IClientChannel)proxy))
{
    OperationContext.Current.OutgoingMessageHeaders.Add(shareableInstanceContextHeader);
    Console.WriteLine("Service returned: " + proxy.Echo("Apple"));
}

((IChannel)proxy).Close();

// Create the second channel
IEchoService proxy2 = channelFactory.CreateChannel();

// Call an operation using the same header that will reuse the shared service instance
using (new OperationContextScope((IClientChannel)proxy2))
{
    OperationContext.Current.OutgoingMessageHeaders.Add(shareableInstanceContextHeader);
    Console.WriteLine("Service returned: " + proxy2.Echo("Apple"));
}

В отличие от других режимов инстантирования, общий режим инстантирования имеет уникальный способ освобождения экземпляров службы. По умолчанию, когда все каналы закрыты для InstanceContext, среда выполнения службы WCF проверяет, настроена ли служба InstanceContextMode на PerCall или PerSession и, если это так, освобождает экземпляр и берет на себя ресурсы. Если используется пользовательская IInstanceContextProvider функция, WCF вызывает IsIdle метод реализации поставщика перед выпуском экземпляра. Если IsIdle возвращает true экземпляр, он освобождается, в противном случае реализация IInstanceContextProvider должна уведомить Dispatcher о состоянии простоя с помощью метода обратного вызова. Это делается путем вызова метода поставщика с помощью NotifyIdle.

В этом примере показано, как можно отложить освобождение InstanceContext с тайм-аутом простоя в 20 секунд.

Расширение InstanceContext

В WCF InstanceContext является связующим элементом между экземпляром службы и Dispatcher. WCF позволяет расширить этот компонент среды выполнения, добавив новое состояние или поведение с помощью его расширяемого шаблона объекта. Шаблон расширяемого объекта используется в WCF для расширения существующих классов среды выполнения с новыми функциями или добавления новых функций состояния в объект. Существует три интерфейса в расширяемом шаблоне объектов: IExtensibleObject<T>, IExtension<T>и IExtensionCollection<T>.

Интерфейс IExtensibleObject<T> реализуется объектами для поддержки расширений, которые настраивают их функциональность.

Интерфейс IExtension<T> реализуется объектами, которые могут быть расширениями классов типа T.

И наконец, IExtensionCollection<T> интерфейс — это коллекция реализаций IExtension<T> , которая позволяет получить реализацию IExtension<T> по их типу.

Таким образом, чтобы расширить InstanceContextинтерфейс, необходимо реализовать IExtension<T> интерфейс. В этом примере проекта CustomLeaseExtension класс содержит эту реализацию.

class CustomLeaseExtension : IExtension<InstanceContext>
{
}

Интерфейс IExtension<T> имеет два метода Attach и Detach. Как видно из их названий, эти два метода вызываются при подключении и отсоединении среды выполнения от экземпляра класса InstanceContext. В этом примере Attach метод используется для отслеживания InstanceContext объекта, который принадлежит текущему экземпляру расширения.

InstanceContext owner;

public void Attach(InstanceContext owner)
{
    this.owner = owner;
}

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

interface ICustomLease
{
    bool IsIdle { get; }
    InstanceContextIdleCallback Callback { get; set; }
}

class CustomLeaseExtension : IExtension<InstanceContext>, ICustomLease
{
}

При вызове метода IsIdle в реализации IInstanceContextProvider на WCF этот вызов маршрутизируется в метод IsIdle объекта CustomLeaseExtension. Затем CustomLeaseExtension проверяет свое частное состояние, чтобы узнать, находится ли InstanceContext в состоянии простоя. Если он неактивен, возвращается true. В противном случае он запускает таймер для указанного периода длительного существования.

public bool IsIdle
{
  get
  {
    lock (thisLock)
    {
      if (isIdle)
      {
        return true;
      }
      else
      {
        StartTimer();
        return false;
      }
    }
  }
}

В событии таймера Elapsed функция обратного вызова в диспетчере вызывается для запуска другого цикла очистки.

void idleTimer_Elapsed(object sender, ElapsedEventArgs args)
{
    lock (thisLock)
    {
        StopTimer();
        isIdle = true;
        Utility.WriteMessageToConsole(
            ResourceHelper.GetString("MsgLeaseExpired"));
        callback(owner);
    }
}

При переводе экземпляра в состояние простоя невозможно обновить текущий таймер при поступлении нового сообщения.

Пример реализует IInstanceContextProvider для перехвата вызовов метода IsIdle и их направления к CustomLeaseExtension. Реализация IInstanceContextProvider содержится в CustomLifetimeLease классе. Метод IsIdle вызывается, когда WCF собирается освободить экземпляр службы. Однако в коллекции ServiceBehavior ISharedSessionInstance существует только один экземпляр конкретной IInstanceContextProvider реализации. Это означает, что нет способа узнать, закрыт ли InstanceContext во время проверки метода IsIdle WCF. Поэтому в этом примере используется блокировка потока для сериализации запросов к методу IsIdle .

Это важно

Использование блокировки потоков не рекомендуется, так как сериализация может сильно повлиять на производительность приложения.

Поле частного члена используется в CustomLifetimeLease классе для отслеживания состояния простоя и возвращается методом IsIdle . Каждый раз при вызове метода IsIdle, поле isIdle возвращается и сбрасывается в false. Важно задать это значение false , чтобы убедиться, что диспетчер вызывает NotifyIdle метод.

public bool IsIdle(InstanceContext instanceContext)
{
    get
    {
        lock (thisLock)
        {
            //...
            bool idleCopy = isIdle;
            isIdle = false;
            return idleCopy;
        }
    }
}

Если метод IInstanceContextProvider.IsIdle возвратит false, диспетчер регистрирует функцию обратного вызова, используя метод NotifyIdle. Этот метод получает ссылку на InstanceContext, которая освобождается. Поэтому пример кода может опрашивать расширение типа ICustomLease и проверить свойство ICustomLease.IsIdle в расширенном состоянии.

public void NotifyIdle(InstanceContextIdleCallback callback,
            InstanceContext instanceContext)
{
    lock (thisLock)
    {
       ICustomLease customLease =
           instanceContext.Extensions.Find<ICustomLease>();
       customLease.Callback = callback;
       isIdle = customLease.IsIdle;
       if (isIdle)
       {
             callback(instanceContext);
       }
    }
}

Перед проверкой свойства ICustomLease.IsIdle, необходимо задать свойство обратного вызова, так как это важно для уведомления диспетчера CustomLeaseExtension, когда он становится неактивным. Если ICustomLease.IsIdle возвращает true, приватный элемент isIdle просто устанавливается в CustomLifetimeLease на true и вызывается метод обратного вызова. Поскольку код содержит блокировку, другие потоки не могут изменить значение этого приватного члена. И при следующем вызове IInstanceContextProvider.IsIdle диспетчер возвращает true и освобождает экземпляр.

Теперь, когда подготовительная работа для пользовательского расширения завершена, его необходимо интегрировать с моделью сервиса. Чтобы подключить CustomLeaseExtension реализацию к InstanceContext, WCF предоставляет IInstanceContextInitializer интерфейс для выполнения начальной загрузки InstanceContext. В примере класс CustomLeaseInitializer реализует этот интерфейс и добавляет экземпляр CustomLeaseExtension в коллекцию Extensions внутри инициализации единственного метода. Этот метод вызывается диспетчером при инициализации InstanceContext.

public void InitializeInstanceContext(InstanceContext instanceContext,
    System.ServiceModel.Channels.Message message, IContextChannel channel)

    //...

    IExtension<InstanceContext> customLeaseExtension =
        new CustomLeaseExtension(timeout, headerId);
    instanceContext.Extensions.Add(customLeaseExtension);
}

Наконец, IInstanceContextProvider реализация подключается к модели службы с помощью IServiceBehavior реализации. Эта реализация помещается в CustomLeaseTimeAttribute класс, а также является производным от Attribute базового класса для предоставления этого поведения в качестве атрибута.

public void ApplyDispatchBehavior(ServiceDescription description,
           ServiceHostBase serviceHostBase)
{
    CustomLifetimeLease customLease = new CustomLifetimeLease(timeout);

    foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
    {
        ChannelDispatcher cd = cdb as ChannelDispatcher;

        if (cd != null)
        {
            foreach (EndpointDispatcher ed in cd.Endpoints)
            {
                ed.DispatchRuntime.InstanceContextProvider = customLease;
            }
        }
    }
}

Это поведение можно добавить в пример класса службы, заметив его атрибутом CustomLeaseTime .

[CustomLeaseTime(Timeout = 20000)]
public class EchoService : IEchoService
{
  //…
}

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

Настройка, сборка и запуск примера

  1. Убедитесь, что вы выполнили процедуру настройкиOne-Time для примеров Windows Communication Foundation.

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

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