Security Context Token(SCT) Expired on WCF

Posted in .NET Framework // Posted at 2015. 3. 3. 18:15

초단위로 반복적으로 WCF를 호출하는 응용프로그램이 있다.

이 WCF 응용프로그램의 바인딩은 wsHttpBinding을 설정하였으며 X.509 인증서기반의 Message 보안모드 상태에서 동작한다. 더불어 클라이언트 인증은 UserName 자격증명을 이용중이다.

 

참고로, WCF는 자체적으로 보안강화를 위해 안전장치를 몇 가지 마련해 두고 있는데, 대표적으로 reply 공격을 방어하기 위해 클라이언트와 서버시간이 일정시간(기본 5분) 이상 차이가 나면 서비스 호출이 불가능하도록 하는 것을 예로 들 수 있다.

 

이런 안전장치들은 WCF 보안 기능을 사용할 경우, 기본적으로 제공되는 것들이다.

따라서 간혹 보안기능에 문제가 발생할때 이런 기본사항을 인지하지 않고 있다면 문제해결이 어려운 경우가 있다. 물론 이러한 기본 보안장치들은 특정 비즈니스 환경에 적합하지 않을 수도 있기에 개발자에 의해 커스터마이징이 가능하다.

 

앞서 초단위로 반복 호출되는 WCF 응용프로그램에서, 어느날 다음과 같은 오류가 발견되었다.

이 오류가 초단위로 지속적으로 나타나고 있었다. 메시지 인증을 지속적으로 시도하여 UserName 자격증명을 처리하는 서비스 오퍼레이션이 불필요하게 많이 호출되는 것이다.

 

메시지 인증을 실행하지 못했습니다.
 서비스: http://mydomain/Service.svc
 동작: http://schemas.xmlsoap.org/ws/2005/02/trust/RST/SCT/Renew
 ClientIdentity:
 ActivityId: <null>
 SecurityContextTokenValidationException: context-id=urn:uuid:xxxxxxx-xxxx-xxxx-81bb-8218dddd5e8f(key generation-id=)을(를) 가진 SecurityContextSecurityToken이 등록되지 않았습니다. 

 

이 상황에서의, 클라이언트 측 HTTP 응답오류는 다음과 같다. 

HTTP/1.1 500 Internal Server Error
Content-Length: 663
Content-Type: application/soap+xml; charset=utf-8
Server: Microsoft-IIS/7.5
X-Powered-By: ASP.NET
Date: Tue, 03 Mar 2015 06:44:16 GMT

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing"><s:Header>
<a:Action s:mustUnderstand="1">http://www.w3.org/2005/08/addressing/soap/fault</a:Action>

<a:RelatesTo>urn:uuid:2def471f-b90d-498b-85b3-edfcaee8dff1</a:RelatesTo></s:Header>
<s:Body><s:Fault><s:Code><s:Value>s:Sender</s:Value><s:Subcode><s:Value xmlns:a="http://schemas.xmlsoap.org/ws/2005/02/sc">a:BadContextToken</s:Value></s:Subcode></s:Code>
<s:Reason><s:Text xml:lang="ko-KR">보안 컨텍스트 토큰이 만료되었거나 잘못되었습니다. 메시지가 처리되지 않았습니다.</s:Text></s:Reason></s:Fault></s:Body></s:Envelope> 

 

WCF Trace Log를 남겨서 조사도해보고, 몇 가지 예상되는 시나리오를 가정하고 테스트를 해 봐도 쉽게 잡히질 않았다. 더욱이 멀티 쓰레드 환경에서 백그라운드로 실행되어, 명시적인 서비스 오퍼레이션 호출에 기인한 메시지 인증의 자동요청이라 패킷검사에서도 서비스 오퍼레이션 이름이 아닌, 인증토큰과 관련된 협상과정만 반복되어 디버깅에 더 애를 먹게 되었다.

 

유독 특정 사용자에게서만 발생하던 오류라 그 사용자의 PC환경과 응용프로그램 사용패턴을 들여다보고, 자료를 좀 찾아보니 예상되는것이 나온다.

 

Idle Session 상태에서 보안토큰의 만료시간으로 인한 토큰 검증에 실패한 것으로 추측된다.

그 사용자는 응용프로그램을 실행한 뒤, 한참을 그냥 두고 PC가 유휴상태에 있다가 다시 활성상태로 돌아와서 다시 사용하는 등 Idle Session 상태를 빈번히 유발하는 것이다.

 

구글링에서 몇 가지 좋은 아이디어를 얻을 수 있었다.

이제 서비스 환경과 정책에 맞도록 idle Session에 대한 보안토큰 만료 완화 혹은 명시적 거부를 구현하기만 하면 될 듯 하다. 이런 오류는 사람을 매우 피곤하게 하지만, 해결이 되면 그마이 기쁜것이 없다.

----------------------------------------------------

http://www.codeproject.com/Articles/156994/WCF-Secure-Channel-cannot-be-opened-Load-Balancing

 

http://stackoverflow.com/questions/912580/invalid-or-expired-security-context-token-in-wcf-web-service

 

http://stackoverflow.com/questions/16575799/invalid-or-expired-security-context-token-when-running-after-a-debugging-resta

 

http://stackoverflow.com/questions/1683724/what-are-the-impacts-of-setting-establishsecuritycontext-false-if-i-use-https

 

https://msdn.microsoft.com/ko-kr/library/ms731814(v=vs.110).aspx

https://msdn.microsoft.com/en-us/library/ms731346(v=vs.110).aspx
https://msdn.microsoft.com/ko-kr/library/ms731346(v=vs.110).aspx


http://forums.asp.net/t/1760793.aspx?establishSecurityContext+false+


https://social.msdn.microsoft.com/Forums/en-US/3faee9bb-e843-42fc-a634-f6feb786c698/expired-security-token?forum=wcf

 

------------------------------------------------------------

 

해당 오류에 대한 클라이언트 측 Exception 은 최초 키 갱신에 대한 오류 이후, MessageSecurityException 즉, 일반적인 메시지보안 오류가 지속된다.

SessionKeyExpiredException 은 private로, 타입비교가 불가능해, 굳이 해당 예외에 맞춤형 처리를 하고자 한다면 문자열로 변환해서 비교하면 될 듯 하다. 

System.ServiceModel.Security.SessionKeyExpiredException: 보안 세션 키를 갱신할 수 없습니다.

Server stack trace:
   위치: System.Runtime.AsyncResult.End[TAsyncResult](IAsyncResult result)
   위치: System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.End(SendAsyncResult result)
   위치: System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result)
   위치: System.ServiceModel.Channels.ServiceChannelProxy.InvokeEndService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
   위치: System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)

 

 System.ServiceModel.Security.MessageSecurityException: 상대방으로부터 보호되지 않거나 잘못 보호된 오류를 받았습니다. 오류 코드 및 세부 정보는 내부 FaultException을 참조하십시오. ---> System.ServiceModel.FaultException: 보안 컨텍스트 토큰이 만료되었거나 잘못되었습니다. 메시지가 처리되지 않

았습니다.
   --- 내부 예외 스택 추적의 끝 ---

Server stack trace:
   위치: System.Runtime.AsyncResult.End[TAsyncResult](IAsyncResult result)
   위치: System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.End(SendAsyncResult result)
   위치: System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result)
   위치: System.ServiceModel.Channels.ServiceChannelProxy.InvokeEndService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
   위치: System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)

InnerException Info>>
System.ServiceModel.FaultException: 보안 컨텍스트 토큰이 만료되었거나 잘못되었습니다. 메시지가 처리되지 않았습니다.

 

'.NET Framework' 카테고리의 다른 글

미스터 빈, 너로구나  (0) 2014.07.31
Caching in WCF  (2) 2014.02.21
ASP.NET SignalR  (0) 2013.12.06
maxConcurrentSessions in WCF  (0) 2013.11.21
SQL Double Split  (0) 2013.11.13

미스터 빈, 너로구나

Posted in .NET Framework // Posted at 2014. 7. 31. 16:57

netTcpBinding 기반의 WCF 서비스에 다수의 (논리적으로 구분할 수 잇는) 서비스가 구성되어 있다.

 

서비스는 IIS 호스팅 환경에 셋팅되어 있는데, (논리적인) 서비스 중 한 놈은 큰 용량의 자료를 클라이언트로부터 수신하여 처리하는 기능을 가지고 있는데 이 놈 때문에 다른 서비스의 질에 영향을 주고 싶지 않아 응용프로그램 풀을 분리하기로 결정했다.

 

IIS 상 웹사이트 하위 디렉터리를 응용프로그램으로 바꾸고 풀을 따로 설정한 후 실행하니 WCF 서비스가 동작하지 않는다.

 

문제는 라이브러리로 만들어진 서비스 dll을 찾지 못하는 것이다.

 

가만히 생각해보니, 하위 폴더를 독립된 응용프로그램 영역으로 만들었으니 이 폴더가 응용 프로그램 도메인 루트가 되어 더이상 웹 사이트 루트의 bin 폴더가 참조되지 않기 때문이다.

 

그래서 bin 폴더를 옮겨놓으니 잘 된다.

그러나 매번 빌드하고 bin 폴더를 옮기는 것은 매우, 아주, 심히, 귀찮은 일이다. 깜빡할 수도 있고...

 

VS의 빌드 이벤트는 이런 상황에 알맞은 해답을 제시해 준다. 바로 빌드이벤트 명령이다.

 

다음과 같이 빌드 후 이벤트 명령줄에 입력한다.

 

xcopy "$(TargetDir)*.*" "$(ProjectDir)\SubDirectory\bin" /Y /I

 

이젠 프로젝트를 빌드하면 하위 폴더에 자동으로 dll 들이 복사되어 신경 쓸 필요가 없게 되었다.

 

 

'.NET Framework' 카테고리의 다른 글

Security Context Token(SCT) Expired on WCF  (0) 2015.03.03
Caching in WCF  (2) 2014.02.21
ASP.NET SignalR  (0) 2013.12.06
maxConcurrentSessions in WCF  (0) 2013.11.21
SQL Double Split  (0) 2013.11.13

Caching in WCF

Posted in .NET Framework // Posted at 2014. 2. 21. 14:42

 

WCF 기반 서비스들이 공통으로 사용할 프레임워크를 만들면서, 오퍼레이션의 결과에 대한 캐싱 기능을 프레임워크 아키텍처에 포함 시키기로 했다.

 

서비스 오퍼레이션이 수행되기 전에 제어권을 받아 분기처리가 가능하도록 지원하는 IOperationInvoker 기반의 Caching Invoker를 제작하는데 캐시 API의 구현을 고민하게 되었다.

 

WCF에서도 ASP.NET의 Cache(System.Web.Caching.Cache)를 사용할 수 있지만, 이를 사용하기 위해서는 ASP.NET 호환성을 위해 서비스 클래스에 AspNetCompatibilityRequirements Attribute를 설정하고 Config 파일에 <serviceHostingEnvironment> 요소 aspNetCompatibilityEnabled 속성을 활성화 시켜 주어야 하는 등 프레임워크 연동에 제약을 가하게 되며, 더불어 이와같은 환경은 IIS 호스팅을 강제하기 때문에 셀프 호스팅 환경에서는 프레임워크의 캐싱 기능을 활용 할 수 없는, 즉 범용성의 저해가 발생해 버린다.

 

따라서 ASP.NET의 Cache는 적절치 못하다는 생각이다.

 

다른 방편으로, 전역 static 변수로 Dictionary 형태의 자료구조를 직접 관리할 수 있으나, 캐시 만료시간과 우선순위, Dependency 및 캐시 상태 변화에 따른 콜백 이벤트 등이 필요할 경우 이를 직접 구현해야 하니 역시 적절치 못하다 하겠다.

 

그러던 중, .NET 4.0에 추가된 MemoryCache라는 넘을 알게 되었다.

MSDN의 다음 한 구절이 나를 흥분(?)하게 만들었다.

ASP.NET Cache MemoryCache 클래스 사이의 주요 차이는 ASP.NET 응용 프로그램이 아닌 .NET Framework 응용 프로그램에서 사용할 수 있도록 MemoryCache 클래스가 변경되었다는 점입니다

 

왜 이걸 이제야 알게 되었단 말인가? 프레임워크 버전업에 따른 기본 API 학습을 게을리 한 탓이다. ㅡ,ㅡ;

MSDN을 찬찬히 훑어보니 딱! 원하던 그것이다.

 

WCF 서비스 기반에서 ASP.NET 호환성과 무관하게 캐싱 기능을 제공하기 위해 사용하기로 했다.

본격적인 적용 이전에 확인해야 할 사항은, 이 캐시가 ASP.NET 캐시처럼 응용 프로그램 도메인 내 전역 메모리를 사용하는가 하는 것이었다.

 

WCF 서비스가 매 호출마다 새로운 인스턴스를 만들거나 Caching Invoker가 매번 새롭게 생성된다면 MemoryCache를 인스턴스 범위를 넘어서도록 관리해야 한다.

 

간단히 테스트를 해보니 자연스럽게 응용 프로그램 도메인내에서 공유가 가능한 영역에서 캐시를 관리할 수 있다는 것을 확인하였다. 기본적인 활용 코드는 대략 다음과 같다.

 

ObjectCache cache = MemoryCache.Default;
CacheItemPolicy policy = new CacheItemPolicy();
policy.AbsoluteExpiration = aAbsoluteExpirationDateTime;
policy.SlidingExpiration = slidingExpirationTimeSpan;

                   

cache.Set(keyName, cacheValue, policy);

 

...

 

cache.Get(keyName);

 

 

'.NET Framework' 카테고리의 다른 글

Security Context Token(SCT) Expired on WCF  (0) 2015.03.03
미스터 빈, 너로구나  (0) 2014.07.31
ASP.NET SignalR  (0) 2013.12.06
maxConcurrentSessions in WCF  (0) 2013.11.21
SQL Double Split  (0) 2013.11.13

ASP.NET SignalR

Posted in .NET Framework // Posted at 2013. 12. 6. 16:34

오늘 우연히 다른 기술 자료를 검색하다, SignalR 이라는 라이브러리를 접하게 되었다. 실시간 웹 기능을 지원해 주는 ASP.NET 기반 오픈소스 라이브러리이다.

 

 

 

 

웹의 실시간 기능을 위한 다양한 기술과 기법이 사용되어 왔으나, HTML5에 와서야 Web Socket를 통한 진정한 양방향 실시간 통신이 가능해 졌다고 할 수 있다. 이 블로그에서도 관련 내용을 다룬바 있다.

 

=> [HTML5] Web Socket

=> [HTML5] Server-Sent Events

 

SignalR은 실시간 기능을 구현하기 위해 Web Socket을 사용하고 있으나, Web Socket를 지원하지 않는 환경을 위한 fallback 도 마련해 두고 있다.(이때 사용되는 기술이 Server-Sent Event, LongPolling과 같은 것들이다.)

(대부분의 환경을 지원하기 위한, 친절한 라이브러리가 아닐 수 없다. ^^)

 

다음 사이트들에서 SignalR의 개념과 소스, 응용 데모를 확인해 볼 수 있다.

 

눈이 번쩍 뜨여, 몇 가지 데모를 실행해 봤더니 놀라울 따름이다. 닷넷과 자바스크립트로 구성되어 실시간 웹 기능을 위한 서버와 클라이언트 측을 포괄하고 있다. 실시간 웹 기능을 위한 생산성과 안정성이 크게 향상될 것으로 보인다.

 

클라이이언트 측 Knockout.js와 묶으면 기가막힌 실시간 싱글 페이지를 매우 쉽게 구현할 수 있을 것 같다.

=> Knockout.js

 

이런 생각을 하고 있던 차에, 다음 블로그의 글을 발견했다. SignalR과 Knockout.js의 조합 뿐만 아니라 서버 측 자동 이벤트 발생을 위한 EF SqlDependency를 한데 묶어 데이터 변경 기반 실시간 웹 기능 사례를 보여주고 있다.

 

=> SignalR + SqlDependency + Knockout JS를 활용한 실시간 데이터 조회

 

환경적인 부분에서 참고할 사항은 SignalR이 어떤 통신 기반(WebSocket? Comet?..)으로 구동하는지는,

브라우저에 따라서도 다르지만 윈도우 서버의 IIS에 호스팅 될 경우 서버 버전도 염두해야 한다.

 

도우 서버 2012 또는 윈도우 8에서 WebSocket 프로토콜을 활성하 시킨 경우에만 웹 소켓을 기반으로 한다는 사실을 참고하자.

 

IIS외에 셀프 호스팅도 가능하니 다음 URL에서 간단한 힌트를 얻을 수 있겠다.

=> Signalr selfhosting

 

시간이 갈수록 개발자가 점점 편해지고 있는 듯 하다. 장/단이 있으며 양면성이 있으나, 개발자가 아닌 툴 이용자로 전락하지 않기 위한 노력이 필요하겠다.

 

'.NET Framework' 카테고리의 다른 글

미스터 빈, 너로구나  (0) 2014.07.31
Caching in WCF  (2) 2014.02.21
maxConcurrentSessions in WCF  (0) 2013.11.21
SQL Double Split  (0) 2013.11.13
Optimizing IIS Performance  (2) 2013.11.07

maxConcurrentSessions in WCF

Posted in .NET Framework // Posted at 2013. 11. 21. 15:05

일전에 WCF 서비스 스로톨링(Service Throttling)의 기본 값을 알아본 바 있다.

=> http://m.mkexdev.net/189

 

 

 

 

 

스로톨링 항목 중, 최대 연결 세션 수를 제한하는 maxConcurrentSessions 라는 속성이 있는데, 이 값이 세션을 사용하지 않는 바인딩 환경에서도 제한값으로 동작하는지 궁금해 졌다.

 

WCF 서적을 보니, 트랜스포트 세션/보안 세션/신뢰 세션 등을 사용하지 않는 바인딩에서는 이 속성은 의미없다라고 설명하고 있으나, 어떤 인터넷 자료에서는 그렇지 않다는 예기가 있다.

 

상식적으로 생각해 봐도, 세션에 기반하지 않는 바인딩이라면 세션 제한 수가 무의미한게 맞는 듯 하지만 확실히 해두기 위해 테스트를 수행해 보았다.

 

두 개의 WCF 서비스를 만들고, 하나는 basicHttpBinding 으로 나머지 하나는 wsHttpBinding 기반의 신뢰세션을 사용하도록 한 뒤 세션 제한 수를 5로 설정하고 클라이언트에서 각각 10번씩 호출해 본다. 이때 세션 제한 수에 걸리도록, 프록시 객체를 닫지(Close) 않도록 한다.

 

//WCF Service 설정

<serviceThrottling maxConcurrentSessions="5" /> 

 

//클라이언트 코드

for (int i = 0; i < 10; i++)
{
       ServiceReference1.Service1Client proxy = new ServiceReference1.Service1Client();                
       Console.WriteLine(string.Format("{0}-{1}",i,proxy.GetData(7)));
       //proxy.Close(); //세션 제한수에 걸리도록 프록시를 닫지 않는다.
}

 

결과는 예상했던 대로, 세션을 사용하지 않는 basicHttpBinding은 maxConcurrentSessions 제한 값과 무관하게 10번 모두 호출되었다. 반면 신뢰 세션을 사용하도록 한 wsHttpBinding은 이 제한값에 영향을 받아 5번 호출되고 대기하다가 결국 Timeout 오류가 발생했다.

 

그래서 이렇게 결론을 내렸다.

: 세션을 사용하지 않는 바인딩 환경에서는 maxConcurrentSessions 제한 값이 무의미하다.

 

좀 더 테스트 해보기 위해, wsHttpBinding에서 신뢰세션을 제거하고 테스트를 해 보기로 했다.

wsHttpBinding에서 신뢰 세션을 제거하면 결국 그 어떤 세션도 사용하지 않게 되므로 basicHttpBinding와 같이 제한 값이 무의미해져야 하는데, 테스트 결과는 여전히 5번 호출 뒤 대기하는 것이 확인되었다.

 

이해할 수 없는 결과로 몇 번의 삽질을 거듭하다가 문득, wsHttpBinding의 기본 보안 모드가 message이고 클라이언트 신원증명이 윈도우계정이라는 것이 떠올라 해법을 찾게 되었다.

 

간혹, wsHttpBinding 바인딩을 사용하는 WCF 서비스를 개발할 때, 본인의 PC에서는 잘 동작하던 것이 서비스를 원격 서버로 이동하면 보안문제가 발생하는 것을 경험해 보았을 것이다. 이것이 바로 wsHttpBinding가 기본적으로 Message 보안 모드에 윈도우 계정에 의한 클라이언트 자격증명이 이뤄지기 때문에 그런 것이다. 즉 로컬 환경에 서비스와 클라이언트가 모두 존재할 경우 윈도우 인증이 자동으로 가능해지기 때문에 로컬에서는 문제가 없었던 것이다. 필자 역시 과거 이러한 경험을 반추하여 위와 같은 해법을 찾게 된 것이다.

 

결국 wsHttpBinding에 기본 모안보드를 변경하기 위해 명시적으로 다음과 같이 설정을 하고 테스트를 해 보니 basicHttpBinding과 동일하게 10번 모두 정상 호출되었다.

<wsHttpBinding>
     <binding name="myBinding">
         <security mode="None"></security>
      </binding>
</wsHttpBinding>

 

즉 결론은 동일하다.
: 세션을 사용하지 않는 바인딩 환경(basicHttpBinding, wsHttpBinding 모두)에서는 maxConcurrentSessions 제한 값이 무의미하다.

(참고: wsHttpBinding 일 경우, 보안모드를 명시적으로 None로 설정해야 보안세션이 사용되지 않는다.) 

 

 

'.NET Framework' 카테고리의 다른 글

Caching in WCF  (2) 2014.02.21
ASP.NET SignalR  (0) 2013.12.06
SQL Double Split  (0) 2013.11.13
Optimizing IIS Performance  (2) 2013.11.07
클라이언트의 동시 연결 수(maxconnection)  (0) 2013.11.06

SQL Double Split

Posted in .NET Framework // Posted at 2013. 11. 13. 18:38

SQL에서 Split 함수 구현은 쉽게 찾을 수 있다. 그런데 프로젝트 진행 중, 내가 필요한 것은 더블 스플릿이다.

(더블 스필릿이라는 공식 명칭은 없다. 그냥 내 맘대로 붙여 먹은 이름이니 어디가서 들먹이면 안됨!)

 

즉 하나의 구분자로 문자들을 분리하고, 분리된 문자에는 또 다시 구분자가 있어 다시 스필릿 해야 하는 구조.

대략 다음과 같이...

 

: key1=value1,key2=value2,key3=value3

 

위 예시에서 첫번째 구분자는 콤마(,)이고 두번째 구분자는 이꼴(=)이다.

이 값을 차례대로 분리해서 테이블 형태로 반환하고 싶은 것이다.

 

과거 훌륭한 DBA로 부터 수급한 싱클 스플릿 함수 구현체에, 꾸역꾸역 더블 스플릿이 가능하도록 끼워넣었다.

사소하게 기뿌다 ^^;

 

ALTER FUNCTION [dbo].[UFN_DoubleSplit] (@STRMORE AS VARCHAR(8000),@STRDELIMETER1 AS VARCHAR(10),@STRDELIMETER2 AS VARCHAR(10)) 
RETURNS @RETURN_TABLE TABLE
(
    idx int identity(1,1)
,   strVALUE1 VARCHAR(500)
,   strVALUE2 VARCHAR(500)
)
AS
BEGIN
DECLARE
    @NINDEX   INT
, @NINDEX2  INT
,   @DEL_LENGTH INT
, @SUBSET  VARCHAR(100)
,   @STRVALUE1  VARCHAR(100)
,   @STRVALUE2  VARCHAR(100)
 
SET @DEL_LENGTH = LEN(@STRDELIMETER1)
 
WHILE LEN(@STRMORE) > 0
    BEGIN
   
        SET @NINDEX = CHARINDEX(@STRDELIMETER1, @STRMORE)
 
        IF (@NINDEX = 0)
        BEGIN      
   SET @NINDEX2 = CHARINDEX(@STRDELIMETER2, @STRMORE)
   SET @STRVALUE1 = SUBSTRING(@STRMORE, 0, @NINDEX2)
   SET @STRVALUE2 = SUBSTRING(@STRMORE, @NINDEX2 + 1, LEN(@STRMORE))      
   INSERT @RETURN_TABLE (strVALUE1,strVALUE2) VALUES (@STRVALUE1,@STRVALUE2)
   RETURN
        END       
        ELSE IF (@NINDEX = 1)
        BEGIN
   SET @STRMORE = SUBSTRING(@STRMORE, @DEL_LENGTH + 1, LEN(@STRMORE))
   CONTINUE
        END
 
        SET @SUBSET = SUBSTRING(@STRMORE, 0, @NINDEX)
        SET @NINDEX2 = CHARINDEX(@STRDELIMETER2, @SUBSET)
        SET @STRVALUE1 = SUBSTRING(@SUBSET, 0, @NINDEX2)
  SET @STRVALUE2 = SUBSTRING(@SUBSET, @NINDEX2 + 1, LEN(@SUBSET))                                               
        INSERT @RETURN_TABLE (strVALUE1,strVALUE2) VALUES (@STRVALUE1,@STRVALUE2) 
                 
        SET @STRMORE = SUBSTRING(@STRMORE, @NINDEX + @DEL_LENGTH, LEN(@STRMORE) - @NINDEX)   
    END
RETURN
END

 

 

2014.02.21 업데이트>>

이 글을 작성하고 난 뒤 'Double Split'를 프로젝트에 적용해서 잘 사용하던 중, Split 해야 할 또 하나의 값이 추가되어 버렸다.

 

즉,  Double Split 기준의 매개변수인 'key1=value1,key2=value2,key3=value3' 형태가 다음처럼 바뀌었다.

: key1=value1&value11,key2=value2&value22,key3=value3&value33

 

그렇다면, Triple Split를 다시 구현할 것인가???

이런.. 유연성이 전혀 없잖어. 또 다른 기준 값이 추가되면 역시 힘겹게 수정해야 한다. 사실 이 글에서 제시한 Split 구현이 그리 깔끔하거나 쉽게 확장가능한 코드가 아니다.

 

결국 XML형태로 매개변수를 입력 받고 파싱해서 테이블로 반환하기로 했다. 구현이 훨씬 깔끔해져 나중에 수정을 하려고 해도 쉽게 가능해 진다.

 

Double Split 이여 안녕~~

 

CREATE FUNCTION [dbo].[UFN_ConvertFromXmlToTable] (@XMLString AS NVARCHAR(MAX))
RETURNS @RETURN_TABLE TABLE
(
    idx int identity(1,1)
,   strValue1 VARCHAR(500)
,   strValue2 NVARCHAR(100)
,   intValue  BIGINT
)
AS
BEGIN
 declare @xml xml = cast(@XMLString as xml)
    
 INSERT @RETURN_TABLE (strValue1,strValue2,intValue)
  select
     x.value('data(@strValue1[1])', 'VARCHAR(500)')
   , x.value('data(@strValue2[1])', 'NVARCHAR(100)')
   , x.value('data((text())[1])', 'BIGINT')
   from
    @xml.nodes('/s') x(x)

RETURN
END

  

 그리고 이렇게 활용하면 된다.

SELECT * FROM dbo.[UFN_ConvertFromXmlToTable]('<s strValue1="1" strValue2="11">1000</s><s strValue1="2" strValue2="22">2000</s><s strValue1="3" strValue2="33">3000</s>')

 

 

'.NET Framework' 카테고리의 다른 글

ASP.NET SignalR  (0) 2013.12.06
maxConcurrentSessions in WCF  (0) 2013.11.21
Optimizing IIS Performance  (2) 2013.11.07
클라이언트의 동시 연결 수(maxconnection)  (0) 2013.11.06
비동기 프로그래밍에서의 메모리 누수  (0) 2013.11.06

Optimizing IIS Performance

Posted in .NET Framework // Posted at 2013. 11. 7. 15:27

다음 글은 MSDN의 다음 글을 발번역한 것이다.

 

> Optimizing IIS Performance

 

IIS 7.0을 기준으로 하고 있지만, 생뚱 맞게 ASP 가이드가 웬말이냐...

어쨋던 ASP.NET에서도 유사한 설정이 있으니 참고할만하며, ASP.NET 가이드 부분은 실질적 도움이 된다.

 

 

 

 

 

IIS 성능 최적화

 

IIS 성능 향상을 위한 구성 옵션 적용

IIS는 성능에 영향을 미치는 다양한 구성 매개변수를 노출한다. 이 토픽에서는 이러한 매개변수 몇 가지를 설명하고 IIS 성능 향상을 위해 필요한 매개변수 설정의 일반적인 지침을 제공한다.

 

* 로그는 필수적인 정보만 저장하거나 전혀 사용하지 않도록 하라.

프로덕션 환경에서는 IIS 로깅을 최소화 하거나 비활성화 해야 한다. 다음 단계를 따르라.

단계 요약: IIS 관리자>해당 사이트>로깅>사용 안 함 설정

 

 

* 프로덕션 환경에서는 IIS ASP 디버깅 기능을 비활성화 시켜라.

프로덕션 환경에서는 IIS ASP 디버깅을 사용하지 말아야 한다. 다음 단계를 따르라.

 

단계 요약: IIS 관리자> 사이트> ASP> 컴파일> 디버깅속성> 서버 쪽 디버깅 사용, 클라이언트 쪽 디버깅 사용 항목을 false로 설정

 

ASP.NET 응용프로그램 또는 웹 서비스의 디버깅 비활성화는 web.config 파일의 <compliation debug=”false” />로 설정

 

 

* 프로세서 당 ASP 스레드 제한 값을 조정하라.

ASP 스레드 제한 속성은 프로세서 당 생성할 수 있는 작업 스레드의 최대값을 나타낸다. 이 값을 프로세서 사용률이 적어도 50% 이상 충족할 때까지 값을 높여라. 이 설정은 웹 응용프로그램의 확장성과 전반적인 서버의 성능에 많은 영향을 미칠 수 있다. 이 값은 동시에 실행할 수 있는 ASP 요청의 최대 값을 정의하기 때문에 당신의 ASP 응용 프로그램이 외부 구성요소로 확장된 요청들을 하지 않는 한 기본 값을 유지해야 한다. 확장된 요청을 하는 경우라면, 값을 높이는 것이 좋다. 이렇게 하면 서버가 더 많은 동시 요청을 처리하기 위해 더 많은 스레드를 만들 수 있다. 기본 값은 25이다. 이 속성의 최대 권장 값은 100이다. 다음 단계를 따르라.

 

단계 요약: IIS 관리자>서버>ASP>제한속성>프로세서 당 스레드 제한 값을 원하는 값으로 설정(기본: 25)

 

IIS 7.0ASP 제한 설정에 대한 더 더 많은 정보는 다음 URL에서 확인

http://www.iis.net/configreference/system.webserver/asp/limits

 

노트1> 이 속성은 서버 레벨에서 적용할 수 있기 때문에, 값의 수정은 서버의 모든 웹 사이트에 영향을 미친다.

노트2> IIS 7.0의 이 속성은 IIS 6.0 ASPProcessorThreadMax ASP Metabase 세팅을 대체한다

 

 

* ASP 큐 길이 조정하라.

이 값을 설정하는 이유는 ASP 요청 큐가 찼을 때 HTTP 503(Server Too Busy)가 반환되는 빈도를 최소화 하면서 좋은 응답성을 보장하기 위함이다. 만일 큐 길이가 너무 낮으면, 서버는 503 오류를 자주 뱉어 내게 될 것이다. 반면 큐 길이가 너무 높으면, 요청이 큐에 대기하는 동안 사용자는 서버가 응답하지 않는다고 인식할 것이다. 높은 트래픽 상황에서 큐를 주의 깊게 살펴보고 피크 타임의 대기 요청 수를 기록하고 이 값보다 높은 값으로 큐 길이를 설정해야 한다. 응답 시간을 보장하고 단기적인 스파이크를 처리하기 위해 큐를 사용하며 지속적인 예상치 못한 스파이크가 발생할 때 과부하를 방지하기 위해 이 값을 조정한다. 당신이 큐 길이 제한에 대한 데이터가 없는 경우, 총 스레드 큐의 일대일 비율로 설정하는 것을 권장한다. 예를 들어 4개의 프로세서 환경에서 프로세서당 ASP 스레드 수의 제한 값이 25일 경우 4 * 25 = 100으로 설정하고 여기서부터 출발하는 것이다. 큐 길이 조정을 위한 다음 단계를 수행하라

 

단계 요약: IIS 관리자>서버>ASP>제한 속성>큐 길이에서 원하는 값 지정

 

노트1> 이 속성은 서버 레벨에서 적용할 수 있기 때문에, 값의 수정은 서버의 모든 웹 사이트에 영향을 미친다.

노트2> IIS 7ASP 큐 길이 속성은 IIS 6.0 AspRequestQueueMax ASP Metabase 셋팅값을 대체한다.

 

 

* MaxPoolThreads 레지스터리 항목을 조정하라.

이 값은 CPU당 풀에 생성할 수 있는 스레드 수를 나타낸다. 풀 스레드는 들어오는 요청을 감시하고 처리한다. MaxPoolThreads 수는 ISAPI 응용프로그램에서 소비하는 스레드는 포함하지 않는다. 일반적으로 프로세서당 20개 이상의 스레드를 생성할 필요는 없다. 이 설정은 레지스터리 HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\InetInfo\Parameters\ REG_DWORD 값으로 지정할 수 있으며 기복 값은 4로 설정되어 있다.

 

 

* WCF 서비스 트레이싱을 비활성화 시켜라.

프로덕션 서버에 WCF 구성 설정 편집기(SvcConfigEditor.exe)를 사용하여 트레이싱을 비활성화 시켜라.

이 툴에 대한 더 자세한 정보는 다음 url에서 확인

http://msdn.microsoft.com/en-us/library/ms732009.aspx

 

 

* IIS 7.0 통합모드를 위한 ASP.NET MaxConcurrentRequests를 구성하라.

IIS 7.0 통합모드에서 ASP.NET 2.0이 호스트 되는 경우, 스레드 처리가 클래식 모드나 IIS 6.0과는 다르게 처리된다. 이 경우 ASP.NET 2.0은 동시 실행 스레드 수 대신 동시 실행 요청 수를 제한한다. 동기 시나리오에서는 요청 수가 스레드 수와 동일하기 때문에 간접적으로 스레드 수를 제한할 것이다. 그러나 비동기 시나리오에서는 스레드 수 보다 훨씬 더 많은 요청이 있을 수 있기 때문에 요청수와 스레드 수가 많이 다를 수 있다. ASP.NET 2.0 IIS7.0 통합모드에서 실행될 경우, machine.config 파일의 httpruntime 요소의 minFreeThreads minLocalRequestFreeThreads 값이 무시된다. IIS 7.0 통합모드에서는 레지스터리의 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET\2.0.50727.0에 있는 MaxConcurrentRequestsPerCPU 값에 의해 CPU당 동시 요청 수를 결정하게게 된다. 기본적으로 이 레지스터리 키는 존재하지 않으며 기본 값은 CPU 12개 이. net framework 3.5 SP1에는 aspnet.config 파일을 통해 IIS 응용프로그램 풀 구성을 할 수있는 2.0 바이너리에 대한 업데이트가 포함되어 있다. 이 파일은 통합모드에서만 유효하다.(IIS 6.0 혹은 클래식 모드일 경우 이 파일의 설정은 무시된다.) 이 새로운 설정 파일의 기본 값은 다음과 같다.

 

<system.web>

   <applicationPool maxConcurrentRequestsPerCPU="12" maxConcurrentThreadsPerCPU="0" requestQueueLimit="5000"/>

</system.web>

 

경험상으로 봤을 때, MaxConcurrentRequestsPerCPU 값은 5000 정도로 큰 값으로 설정해야 한다.

IIS 7.0 통합모드에서는 machine.config 파일의 processModel 요소의 maxWorkerThreads, maxIOThreads 설정은 본질적으로 실행중인 요청 수를 제어하는데 사용되지 않는다. 그러나 여전히 ASP.NET에 의해 사용되는 CLR 스레드 풀의 사이즈를 제어하는 데는 사용된다.

machine.config 파일의 processModel 섹션의 autoConfig 설정이 true(기본 값)일 경우, 응용프로그램 풀에 (논리적인) CPU당 최대 100까지의 스레드(MaxWorkerThreads)를 생성할 수 있다. 2 듀얼 코어 CPU일 경우에는 400개가 된다. This should be sufficient for all but the most demanding applications.

 

IIS 7.0 6.0 ASP.NET 스레드 활용법을 위한 더 많은 정보는 다음 사이트에서 확인 하시길.

http://msdn.microsoft.com/en-us/library/ee377050%28v=bts.10%29.aspx

 

 

* HTTP 압축을 사용하라.

대역폭을 보다 효율적으로 사용하고 싶다면 IIS HTTP 압축을 활성화하라. 압축을 사용하면 콘텐트가 로컬 저장소에 있거나 UNC 리소스이거나 관계없이 (압축을 허용하는) 브라우저와 IIS 간 더 빠른 전송 효과를 볼 수 있다.

(단계 요약: IIS 관리자>서버(또는 사이트)>압축>원하는 압축 옵션 설정)

 

 

 

'.NET Framework' 카테고리의 다른 글

maxConcurrentSessions in WCF  (0) 2013.11.21
SQL Double Split  (0) 2013.11.13
클라이언트의 동시 연결 수(maxconnection)  (0) 2013.11.06
비동기 프로그래밍에서의 메모리 누수  (0) 2013.11.06
OAuth 2.0 Flow  (0) 2013.09.05

클라이언트의 동시 연결 수(maxconnection)

Posted in .NET Framework // Posted at 2013. 11. 6. 16:41

응용프로그램의 성능 저하 문제는 사소하게 간과한, 몇 가지 이유로 유발되는 경우도 있다.

여기서 언급하고자 하는 것도 그러한 것 중 하나이다.

 

바로 클라이언트의 동시 연결 수 제한에 따른 성능 저하 현상이다. 닷넷 응용프로그램에서 원격 호스트와 통신할 때 적용되는 동시 연결 수의 제한인데, 특별히 값을 지정하지 않는 경우(기본 값) 2개의 연결만 허용하게 된다.

 

보편적인 (단순한) 환경이라면 이 제한 값은 문제 되지 않을 것이다. 하지만 매우 바쁜 호스트가 멀티 쓰레드 환경에서 원격시 서버와 통신을 빈번히 하는 경우라면 예기가 달라진다.

 

특히 두 호스트가 Server to Server 방식이라면 이 제한 값은 문제될 소지가 높아진다. 

 

예를 들어, A 서버가 클라이언트의 요청을 받아서 작업을 수행하는데 이 작업 중에서 원격지 서버와 통신하는 부분이 있다고 가정해보자. 이때 A 서버는 그 자체가 서버 응용프로그램이기 때문에 멀티 쓰레드 환경에서 요청을 처리하게 되는 경우(이 말은 꼭 개발자가 직접 작성한 멀티 쓰레드 프로그램만을 말하는 것은 아니다. 프로그램이 얹혀지는 호스팅 환경 자체에서 풀을 기반으로 동시 처리를 하게 되는 경우도 포함한다. 이러한 사례는 웹 응용프로그램이 대표적이다.)가 많으며 요청이 과도하게 몰릴 경우 원격 서버와 통신이 그 만큼 빈번하게 이뤄지게 된다. 이런 환경에서 최대 동시 연결 수가 2개 밖에 되지 않는다면 병목 현상으로 이어질 확률이 높다. 특히 원격지 서버의 작업 수행 시간이 길어진다면 연결을 위한 대기 시간은 점점 늘어나게 될 것이다.

 

더욱 문제는 이 값의 설정이 기본적으로 노출되어 있지 않다는 데 있다. 닷넷 응용프로그램의 config 파일에는 몇 가지 기본 설정들이 자동으로 포함되는데 이 설정 값은 그렇지 않아, 기본 값인 2가 사용되도록 되어 있다.

 

이러한 이유로 많은 개발자들이 이 값의 설정을 간과하게 되며, 심지어 이러한 설정이 있는지 모르는 경우도 많다. 복잡한 환경에서 서버의 응답 속도가 느려 진다는 것은 참으로 많은 구간을 확인해야 하는 일이다. 네트워크, 시스템, DB, 응용프로그램 등 다양한 구간에 가능성을 두고 분할 정복해야 한다.

 

특히 Server to Server 통신 환경에서, 원격 서버의 응답 지연 현상이 반드시 원격 서버만의 문제가 아닐 수 있다는 가능성도 열어 두길 바란다. 앞서 시나리오라면 클라이언트의 설정 값의 변경만으로도 성능 개선이 많이 이뤄질 수도 있다. (물론 원격 서버의 응답시간 자체는 허용 범위 내에 있다는 가정을 둔다.)

 

이런 시나리오를 간단히 테스트 해 보자. 

 

* 원격 서버: ASP.NET MVC Web API 로 기본 Getter 구현. 의도적으로 응답을 지연 시킨다.

 

* 클라이언트: 콘솔 어플리케이션으로 멀티 쓰레드로 원격 서버의 Getter 호출(대략 다음의 코드)

   for (int i = 0; i < 20; i++)
   {
           Thread thread = new Thread(new ThreadStart(Start));
            thread.Start();  //원격 서버 호출하는 스레드 시작
    }

 

 

1. 기본 값을 사용한 경우

특별히 maxconnection 설정을 추가하지 않은 경우, netstat 나 TCPView 프로그램으로 연결 수를 확인해 보면 20개의 요청이 동시에 이뤄지지만, 연결 수는 단 2개만 존재하는 것을 확인할 수 있다.

 

 

 

2. maxconnection을 20으로 설정한 경우

<system.net>
    <connectionManagement>
       <add address="*" maxconnection="20"/>
    </connectionManagement>
</system.net>

 

클라이언트 프로그램의 설정 파일에 위와 같이 maxconnection을 설정하고 다시 확인해 보면 20개의 요청만큼 동시 연결이 되어 있는 것을 확인할 수 있다.

 

 

앞서 2개의 연결만으로 20개의 원격 호출을 처리하는 것보다, 20개의 연결으로 20개의 원격 호출을 병렬적으로 처리하는 것이 훨씬 응답성을 높일 것이다.

 

참고로 이 값의 정해진 규칙은 없다. 응용프로그램의 시스템 환경과 사용량을 파악해 적절한 값을 지정해야 할 것이다. 다만 Server to Server 방식에서 멀티 쓰레드로 원격 통신이 빈번한 환경이라면 기본 값인 '2'는 너무 작은 값임에는 분명하다 하겠다. 

 

 

'.NET Framework' 카테고리의 다른 글

SQL Double Split  (0) 2013.11.13
Optimizing IIS Performance  (2) 2013.11.07
비동기 프로그래밍에서의 메모리 누수  (0) 2013.11.06
OAuth 2.0 Flow  (0) 2013.09.05
Custom Configuration  (6) 2013.08.26

비동기 프로그래밍에서의 메모리 누수

Posted in .NET Framework // Posted at 2013. 11. 6. 11:24

응용프로그램의 성능, 확장성 향상의 이유로 비동기 프로그래밍 방식이 자주 사용되곤 한다.

 

닷넷 프레임워크에서도 스레드(전용 스레드 or 스레드 풀) 혹은 비동기 프로그래밍 모델을 활용해 비동기 작업을 수행할 수 있다.

 

여기서 비동기 프로그래밍 모델을 APM(Asynchronous Programming Model)이라 칭하는데, 닷넷 프레임워크의 I/O 관련 클래스들은 모두 이 모델을 이용할 수 있도록 설계되어 있다.

 

 

I/O를 위한 동기 방식 메서드 이름과 동일한 BeginXXX, EndXXX 쌍의 비동기 메서드가 제공되는데, 다음과 같은 I/O 클래스들에서 이를 활용할 수 있다.

 

System.Net.WebRequest

System.IO.Stream

System.Data.SqlClient.SqlCommand

System.Net.Sockets.Socket

 

필자의 회사에서 운영하는 시스템도 많은 곳에시 비동기 패턴을 이용하고 있다. 그 중 한 예로, 여러 시스템이 혼재되어 하나의 서비스를 완성하는데 이때 각 시스템의 구간 안정성 현황을 파악하기 위해 원격 서버로 로그를 전송하는 로직이 비동기로 구현되어 있다.

 

그런데 이 로그는 실제 비지니스와는 무관한 부분이라(크로스 커팅 기능), 종속성을 배제하기 위해 로그 서버는 단방향 서비스로 구현하고 이를 소비하는 클라이언트는 APM 방식으로 구현되어 있다.

 

그런데 문제는 로그 저장의 성공/실패에 관심을 두지 않는다는 것이, 비동기 호출 후 콜백을 받지 않는 구현으로 이어졌다는 것이다. 즉 BeginXXX만 호출하고 EndXXX는 구현하지 않은 것이다.

 

사용량이 많지 않는 환경에서는 이것이 그리 큰 문제가 되지는 않는다. 하지만 필자의 시스템 환경은 Server to Server 방식으로 로그서버를 소비하는 클라이언트 역시 서버 응용프로그램이며 이 서버 응용프로그램에 수많은 요청이 동시에 몰리면 메모리 누수는 점차 누적되어 프로그램이 심각한 타격을 입을 수 있다는 것이다.

 

다음의 글에서 밝히고 있듯이, 비동기 호출 결과에 관심이 없다고 해서 EndXXX를 호출하지 않아도 된다는 것은 아니니 주의해야 할 일이다.

 

개발자는 EndXXX 메서드를 꼭 호출해야 한다. 그렇지 않으면 리소스가 누수되기 때문이다. 일부 개발자는 데이터 장치에 자료를 저장하기 위해서 BeginXXX를 호출하고, 이후의 결과 값이나 진행 상태에 관심이 없다면 EndXXX를 호출하지 않는 경우를 종종 봐왔다. 하지만 EndXXX를 호출해야 하는 이유는 두 가지가 있다.

 

첫째 CLR은 이 리소스들을 EndXXX 메서드가 호출될 때까지 유지시킨다. 그리고 EndXXX 메서드가 결국 호출되지 않는다면 이 리소스들은 계속 반환되지 않다가 프로세스가 종료될 때에 반환된다. 둘째, 비동기 작업을 초기화할 때 실제로 개발자는이 작업이 성공할 것인지 알지 못한다. 성공유무를 확실하게 확인하는 방법은 EndXXX 메서드를 호출하는 것이며, 이 메서드를 호출함으로써 결과 값이 정상적으로 반환되는지 아니면 예외가 발생하는지 알 수 있다.

- CLR via C#

 

 

'.NET Framework' 카테고리의 다른 글

Optimizing IIS Performance  (2) 2013.11.07
클라이언트의 동시 연결 수(maxconnection)  (0) 2013.11.06
OAuth 2.0 Flow  (0) 2013.09.05
Custom Configuration  (6) 2013.08.26
간단한 C# 문법 Quiz  (1) 2013.07.11

OAuth 2.0 Flow

Posted in .NET Framework // Posted at 2013. 9. 5. 16:45

OAuth 2.0 스펙이 이전 버전과 비교해 덩치가 많이 커져 버린 단점(?)이 있지만, 기본 연동 모델 자체는 보다 심플해진 것 같다. 데이터의 암호화와 무결성 체크를 HTTS에 의존하기 때문에 보안을 위한 추가 장치들이 제거된, 보다 심플해진 흐름을 볼 수 있다.