다음을 통해 공유


사용자 지정 수명

Lifetime 샘플 공유 WCF(Windows Communication Foundation) 서비스 인스턴스에 대한 사용자 지정 수명 서비스를 제공하기 위해 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 서비스 런타임은 서비스 InstanceContextModePerCall 또는 PerSession으로 구성되어 있는지 확인하고 이렇게 구성되어 있으면 인스턴스를 해제하고 리소스를 클레임합니다. 사용자 지정 IInstanceContextProvider를 사용하는 경우 WCF는 인스턴스를 해제하기 전에 공급자 구현의 IsIdle 메서드를 호출합니다. IsIdletrue를 반환하면 인스턴스가 해제되고, 그렇지 않으면 IInstanceContextProvider 구현은 콜백 메서드를 사용하여 Dispatcher에 유휴 상태를 알려야 합니다. 이 작업은 공급자의 NotifyIdle 메서드를 호출하여 수행됩니다.

이 샘플에서는 유휴 시간 제한 20초를 사용하여 InstanceContext 해제를 지연시키는 방법을 보여 줍니다.

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> 인터페이스에는 AttachDetach의 두 가지 메서드가 있습니다. 이름이 나타내듯이 이 두 메서드는 런타임에서 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
{
}

WCF에서 IInstanceContextProvider 구현의 IsIdle 메서드를 호출할 경우 이 호출은 CustomLeaseExtensionIsIdle 메서드로 라우팅됩니다. 그런 다음, 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의 IInstanceContextProvider 컬렉션에는 특정 ISharedSessionInstance 구현의 인스턴스가 하나만 있습니다. 즉, WCF에서 IsIdle 메서드를 확인할 때 InstanceContext가 닫히는지 알 수 있는 방법이 없습니다. 따라서 이 샘플에서는 스레드 잠금을 사용하여 IsIdle 메서드에 대한 요청을 직렬화합니다.

Important

serialization은 애플리케이션의 성능에 큰 영향을 주므로 스레드 잠금은 사용하지 않는 것이 좋습니다.

프라이빗 멤버 필드는 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 속성을 확인하기 전에 Callback 속성이 설정되어야 하며, 이는 CustomLeaseExtension이 유휴 상태가 될 때 디스패처에 이를 알리기 위해서 반드시 필요한 사항입니다. ICustomLease.IsIdletrue를 반환하는 경우에는 isIdle에서 전용 멤버 CustomLifetimeLeasetrue로 설정되고 callback 메서드를 호출합니다. 코드에서 잠금을 유지하므로 다른 스레드에서는 이 프라이빗 멤버의 값을 변경할 수 없습니다. 다음에 디스패처에서 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
{
  //…
}

샘플을 실행하면 작업 요청 및 응답이 서비스와 클라이언트 콘솔 창 모두에 표시됩니다. 서비스와 클라이언트를 종료하려면 각 콘솔 창에서 Enter 키를 누릅니다.

샘플을 설치, 빌드 및 실행하려면

  1. Windows Communication Foundation 샘플의 일회성 설치 절차를 수행했는지 확인합니다.

  2. C# 또는 Visual Basic .NET 버전의 솔루션을 빌드하려면 Building the Windows Communication Foundation Samples의 지침을 따릅니다.

  3. 단일 컴퓨터 또는 다중 컴퓨터 구성에서 샘플을 실행하려면 Windows Communication Foundation 샘플 실행의 지침을 따릅니다.