Microsoft MVP성태의 닷넷 이야기
.NET Framework: 182. WCF의 InactivityTimeout [링크 복사], [링크+제목 복사],
조회: 22121
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 4개 있습니다.)
WCF의 InactivityTimeout


"유수석의 WCF 바이블", 다들 읽어보셨죠?
WCF 실무에 뛰어드시기 전에 꼭 한번 읽어보시기를 바랍니다. (사실 저도 ^^; 아직 9장까지밖에 못 읽었습니다.)

어쨌든! 저 역시 위의 책을 읽으면서 많이 배우고 있습니다. 예를 들어 FaultException 예외를 사용하면 세션이 Faulted 상태로 빠지지 않는다는 사실을 몰랐었고, WCF가 지원하는 세션을 크게 "트랜스포트 세션", "보안 세션", "신뢰할 수 있는 세션"으로 나눈다는 것을 머릿속으로 정리해놓지는 못했었습니다.

그런데,,, 옥의 티가 하나 있는데요. 바로 "신뢰할 수 있는 세션"의 "InactivityTimeout" 설명이 완벽하지 못하다는 정도! (완벽?)

그 책의 636페이지에 보면 아래와 같은 설명이 있는데요.

"
신뢰할 수 있는 세션은 자체적으로 타임아웃을 InactivityTimeout 속성을 통해 설정할 수 있다. 이 타임아웃 시간 동안 클라이언트가 서비스를 호출하지 않으면 신뢰할 수 있는 세션은 종료된다.
... [생략] ...
ReceiveTimeout과 InactivityTimeout 속성이 모두 설정될 수 있으므로 신뢰할 수 있는 세션에 실제로 적용되는 타임아웃 시간은 두 속성값 중 작은 값으로 결정됨을 알아두자.
"



위의 설명에 의하면, "ReceiveTimeout 설정과 InactivityTimeout 속성은 동일한 기능을 하는데, 클라이언트가 서버에 대해 지정된 ReceiveTimeout 또는 InactivityTimeout 시간 동안 호출하지 않으면 세션이 종료된다."로 요약될 수 있습니다. 단지, "트랜스포트 세션"에서는 ReceiveTimeout 값이 사용되고, "신뢰할 수 있는 세션"에서는 부가적으로 InactivityTimeout 값이 하나 더 제공된다는 정도의 의미로 해석됩니다.

하지만, 실제로 테스트 해보면 이 설명이 사실과 다르다는 것을 알 수 있습니다.

예전에 제가 아래의 글에서 InactivityTimeout 값을 설명한 적이 있었는데요.

WCF : netTcpBinding에서의 각종 Timeout 값 설명
; https://www.sysnet.pe.kr/2/0/536

그 때는 말로만 설명하는 것으로 그쳤지만, 마침 질문해 주신 분도 있고 하니 실험적으로 알 수 있는 방법을 제공하도록 하겠습니다.




이를 테스트 해보기 위해, 세션 사용이 가능한 netTcpBinding 종단점을 제공하는 서비스를 만들고 config에 다음과 같이 ReceiveTimeout, InactivityTimeout을 설정합니다.

<netTcpBinding>
    <binding name="netTcpBindingConfig" receiveTimeout="00:01:30" >
        <security mode="None"/>
        <reliableSession enabled="true" inactivityTimeout="00:00:10" ordered="true" />
    </binding>
</netTcpBinding>

서비스의 메서드는 간단하게 Echo 메서드 하나만 정의하고 다음과 같이 시간 출력을 합니다.

public void Echo()
{
    Console.WriteLine("Echo: " + DateTime.Now.ToString("HH:mm:ss"));

    OperationContext.Current.Channel.Closed += new EventHandler(Channel_Closed);
    OperationContext.Current.Channel.Faulted += new EventHandler(Channel_Faulted);
}

void Channel_Faulted(object sender, EventArgs e)
{
    Console.WriteLine("Endpoint Faulted: " + DateTime.Now.ToString("HH:mm:ss"));
}

void Channel_Closed(object sender, EventArgs e)
{
    Console.WriteLine("Endpoint Closed: " + DateTime.Now.ToString("HH:mm:ss"));
}

그리고, 클라이언트에서는 바인딩 값에 아래와 같이 시간을 다르게 설정합니다.

netTcpBinding.ReliableSession.Enabled = true;
netTcpBinding.ReliableSession.InactivityTimeout = new TimeSpan(0, 0, 15);

이제, 서비스를 시작하고 클라이언트에서 접속해서 Echo 메서드를 호출하면 다음과 같이 출력되고,

D:\...\bin>ConsoleApplication1.exe
Press any key to exit...
Echo: 02:09:45

클라이언트에서 연결을 닫지 않은 체로 1분 30초가 지나면 서버 측의 ReceiveTimeout에서 지정한 데로 연결이 끊깁니다. 그래서 최종적으로 아래와 같은 출력 결과를 얻을 수 있습니다.

D:\...\ConsoleApplication1\bin>ConsoleApplication1.exe
D:\...\bin>ConsoleApplication1.exe
Press any key to exit...
Echo: 02:09:45
Endpoint Closed: 02:11:15

보시는 것처럼, 위와 같은 결과를 얻기 위해 정확하게 1분 30초가 소요되었습니다. 만약, 책의 내용대로라면 10초만에 연결이 끊겼어야 합니다.

자, 이제 그럼 InactivityTimeout 값을 테스트 해볼까요?

서버 측은 여전히 동일하게 실행하고, 클라이언트는 실행 후 연결을 닫지 않고 "Ctrl + C" 키를 누르거나 혹은 작업관리자를 통해서 강제 종료를 해봅니다. 이번에는 서버와 클라이언트에서 다음과 같이 동작하는 것을 확인할 수 있습니다.

wcf_inactivityTimeout_test_1.png

보시는 것처럼, 클라이언트는 2시 13분 07초에 프로그램을 (비정상) 종료했고 서버는 정확히 InactivityTimeout 값에 지정된 10초 후인 2시 13분 17초에 클라이언트와의 연결을 종료시켰습니다.

자, 그럼 이번엔 반대로 서버를 강제 종료해봅니다.

wcf_inactivityTimeout_test_2.png

서버가 (비정상) 종료한 시간은 2시 16분 03초이고, 클라이언트는 14초 걸려서 연결을 Faulted로 돌렸는데 이 값은 클라이언트 측의 코드를 통해 지정한 15초에 가까운 값입니다.

이 정도면, InactivityTimeout 값에 대해 알아볼 수 있는 실제적인 실험이었다고 볼 수 있겠지요.

테스트 한 프로젝트를 올려두었으니 각자 로컬에서 테스트 해보시고 의심스러운 부분이 있으면 질문 주시기 바랍니다.




제가 위에서 "완벽"하지 못하다고 말씀을 드렸는데요. 위의 테스트 결과대로라면 "완벽"이 아니라 아예 틀렸다고 볼 수 있는데, 왜 그런 단어를 사용했을까요? 궁금하지 않으세요? ^^

InactivityTimeout 값이 매우 헷갈리는 것이 하나 있는데요. 바로 서버 측의 InactivityTimeout 값을 10초 미만으로 조정하면 그 동작이 ReceiveTimeout과 동일하다는 이상한(?) 규칙을 가지고 있다는 점입니다. 즉, 위의 테스트에서 서버 측의 InactivityTimeout 값을 6초로 지정하면, 6초 내에 클라이언트로부터 호출이 없으면 연결이 끊기게 됩니다.

엄청 재미있는 사실은, 클라이언트 측의 InactivityTimeout 값을 10초 미만으로 지정하는 것은 무관하다는 것입니다. 즉 서버 측에 대해서만 10초 미만으로 지정한 경우 ReceiveTimeout처럼 동작합니다.

제 생각에는 아마도 이것은 버그가 아닐까 생각됩니다. 일관성을 위해서 InactivityTimeout 값을 10초 미만으로 지정해도 위의 테스트 결과대로 나와야 합니다. (버그가 아니라면, 아마도 상대편이 죽은 경우 곧바로 자원을 회수해야 하는 특별한 서비스를 위해서 10초 미만으로 지정되는 경우에 한해 그와 같은 동작을 일부러 하도록 한 것이 아닐런지...!)

아무튼, 이런 이상한 현상으로 인해 10초 미만에 대해서는 책에서 설명한 내용이 맞기 때문에 "완벽"하지 않다고 표현한 것입니다.

참고로, .NET 3.5/4.0에서 모두 위와 같이 10초 현상을 겪고 있습니다.




좀 더 부가적인 설명을 드리자면, 위의 InactivityTimeout 실험 데이터는 아주 이상적인 경우에 맞아 떨어진 수치입니다. 왜냐하면, InactivityTimeout 값을 판단하기 위한 서버와 클라이언트의 heart-beat 체크가 정확히 1초마다 이뤄지는 것은 아니기 때문입니다. 확인을 위해 소켓 API를 가로채기 해보면 다음과 같은 결과를 얻을 수 있습니다.

[Srv] Send: 03시 01분 19초
[Cln] Send: 03시 01분 19초
[Srv] Send: 03시 01분 26초
[Cln] Send: 03시 01분 26초
[Srv] Send: 03시 01분 34초
[Cln] Send: 03시 01분 34초
[Srv] Send: 03시 01분 41초
[Cln] Send: 03시 01분 41초

서버(Srv)와 클라이언트(Cln) 모두 서로를 대략 7초 간격으로 확인하는 것을 볼 수 있는데요. 7초 간격은 클라이언트 측의 15초에서 절반으로 나뉜 시간입니다. 만약 18초로 설정하면 9초 간격으로 서로 체크하는 것이 확인됩니다.

테스트에 의하면, heart-beat을 체크하는 주기는 "서버와 클라이언트 중 큰 시간의 절반"과 "서버와 클라이언트 중 작은 값"을 비교해서 낮은 값으로 설정하게 됩니다.

예를 들어, 서버는 15초, 클라이언트는 20초라고 설정되어 있다면 "20 / 2 = 10초 < 15초"이기 때문에 10초로 선택되어 클라이언트, 서버 양측 모두 10초 단위로 서로 살아 있는지를 확인하게 됩니다.

(물론, 이 실험값은 공식적으로 문서화된 것이 아니기 때문에 향후 닷넷 프레임워크에서는 달라질 수 있겠고!)

그래서, 정확하게 상대편에서 종료한 시점부터 7초가 아니고, 체크하는 바로 그 시점에 상대편이 살아있어야 합니다. 즉, 위의 소켓 가로채기에서 보면 클라이언트가 03시 01분 37초에 종료했다면 그로부터 7초 후에 서버가 연결을 끊는 것이 아니고, 03시 01분 41초 - 체크를 하는 그 시점에 확인 후 곧바로 연결을 종료하게 됩니다.

한 가지 더, 위의 heart-beat 주기는 ReceiveTimeout에도 적용됩니다. 이 때문에, ReceiveTimeout 값이 1분 30초이고, InactivityTimeout 설정의 영향으로 heart-beat이 10초로 설정되어 있다면 최대 1분 40초까지 ReceiveTimeout이 적용될 수 있습니다. 즉, 03시 01분 10초에 마지막으로 클라이언트로부터 호출을 받았던 서비스인 경우 heart-beat 확인이 03시 02분 39초에 되었다면 그 시점에는 1분 30초가 지난 것이 아니기 때문에 연결이 끊기지 않다가 다음번 heart-beat 확인이 되는 03시 02분 49초가 되어서야 연결이 끊기게 됩니다.



[이 토픽에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 7/12/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2010-07-31 12시28분
참... 이런 의미에서, 책의 790페이지에 있는 "리스트 9-17"의 코드가 변경되어야 합니다.

CommuicationState 상태는 클라이언트가 비정상 종료(또는 네트워크가 끊겼다거나)했을 때는 알아낼 수 없기 때문입니다. 즉, InactivityTimeout으로 인해 또는 ReceiveTimeout으로 인해 클라이언트 연결이 정리되기 이전에 CommuicationState 상태를 알아내려고 시도하면 Opened로 나오기 때문에 그런 상황에서는 callback.OnTick 메서드 호출이 예외가 발생할 수 있습니다.

즉, 그 부분도 try/catch로 감싸야 한다는!
kevin25

1  2  3  4  5  6  7  8  9  10  [11]  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13358정성태5/17/20233819.NET Framework: 2125. C# - Semantic Kernel의 Semantic Memory 사용 예제 [1]파일 다운로드1
13357정성태5/16/20233601.NET Framework: 2124. C# - Semantic Kernel의 Planner 사용 예제파일 다운로드1
13356정성태5/15/20233926DDK: 10. Device Driver 테스트 설치 관련 오류 (Code 37, Code 31) 및 인증서 관련 정리
13355정성태5/12/20233859.NET Framework: 2123. C# - Semantic Kernel의 ChatGPT 대화 구현 [1]파일 다운로드1
13354정성태5/12/20234120.NET Framework: 2122. C# - "Use Unicode UTF-8 for worldwide language support" 설정을 한 경우, 한글 입력이 '\0' 문자로 처리
13352정성태5/12/20233747.NET Framework: 2121. C# - Semantic Kernel의 대화 문맥 유지파일 다운로드1
13351정성태5/11/20234229VS.NET IDE: 185. Visual Studio - 원격 Docker container 내에 실행 중인 응용 프로그램에 대한 디버깅 [1]
13350정성태5/11/20233506오류 유형: 859. Windows Date and Time - Unable to continue. You do not have permission to perform this task
13349정성태5/11/20233800.NET Framework: 2120. C# - Semantic Kernel의 Skill과 Function 사용 예제파일 다운로드1
13348정성태5/10/20233700.NET Framework: 2119. C# - Semantic Kernel의 "Basic Loading of the Kernel" 예제
13347정성태5/10/20234095.NET Framework: 2118. C# - Semantic Kernel의 Prompt chaining 예제파일 다운로드1
13346정성태5/10/20233928오류 유형: 858. RDP 원격 환경과 로컬 PC 간의 Ctrl+C, Ctrl+V 복사가 안 되는 문제
13345정성태5/9/20235325.NET Framework: 2117. C# - (OpenAI 기반의) Microsoft Semantic Kernel을 이용한 자연어 처리 [1]파일 다운로드1
13344정성태5/9/20236504.NET Framework: 2116. C# - OpenAI API 사용 - 지원 모델 목록 [1]파일 다운로드1
13343정성태5/9/20234375디버깅 기술: 192. Windbg - Hyper-V VM으로 이더넷 원격 디버깅 연결하는 방법
13342정성태5/8/20234285.NET Framework: 2115. System.Text.Json의 역직렬화 시 필드/속성 주의
13341정성태5/8/20234014닷넷: 2114. C# 12 - 모든 형식의 별칭(Using aliases for any type)
13340정성태5/8/20234088오류 유형: 857. Microsoft.Data.SqlClient.SqlException - 0x80131904
13339정성태5/6/20234769닷넷: 2113. C# 12 - 기본 생성자(Primary Constructors)
13338정성태5/6/20234292닷넷: 2112. C# 12 - 기본 람다 매개 변수파일 다운로드1
13337정성태5/5/20234784Linux: 59. dockerfile - docker exec로 container에 접속 시 자동으로 실행되는 코드 적용
13336정성태5/4/20234592.NET Framework: 2111. C# - 바이너리 출력 디렉터리와 연관된 csproj 설정
13335정성태4/30/20234691.NET Framework: 2110. C# - FFmpeg.AutoGen 라이브러리를 이용한 기본 프로젝트 구성 - Windows Forms파일 다운로드1
13334정성태4/29/20234343Windows: 250. Win32 C/C++ - Modal 메시지 루프 내에서 SetWindowsHookEx를 이용한 Thread 메시지 처리 방법
13333정성태4/28/20233768Windows: 249. Win32 C/C++ - 대화창 템플릿을 런타임에 코딩해서 사용파일 다운로드1
13332정성태4/27/20233870Windows: 248. Win32 C/C++ - 대화창을 위한 메시지 루프 사용자 정의파일 다운로드1
1  2  3  4  5  6  7  8  9  10  [11]  12  13  14  15  ...