Microsoft MVP성태의 닷넷 이야기
.NET Framework: 239. IHttpHandler.IsReusable 속성 이야기 [링크 복사], [링크+제목 복사],
조회: 27629
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일

IHttpHandler.IsReusable 속성 이야기

검색을 해보니, 몇몇 사이트에서 이에 대한 속성을 잘못 설명하는 듯 싶어서 정리를 해볼까 합니다. ^^

우선, 속성의 이름에서 알 수 있듯이 IsReusable은 해당 IHttpHandler를 구현한 인스턴스를 Pool에 보존할 지, 아니면 GC 수집 대상이 될 지를 결정하는 속성입니다.

우리가 흔히 알고 있는 aspx 웹 페이지의 기반 클래스인 Page 타입도 역시 IHttpHandler를 구현한 핸들러의 일종입니다. Page Handler의 경우, 기본값으로 IsReusable == false로 되어 있으며 virtual로 구현되어 있지 않아 이에 대한 값을 하위 클래스에서 재정의할 수 없습니다.

보통 IHttpHandler를 직접 구현하는 가장 쉬운 방법은 IHttpHandler에 대해 기본적인 .ashx 확장자가 등록된 "Generic Handler"를 사용하면 됩니다.

more_detail_about_IHttpHandler_IsReusable_1.png

확장자가 .ashx로 된 IHttpHandler 구현 타입의 장점은 별도의 web.config에 등록될 필요가 없다는 것입니다. 왜냐하면, 이미 .ashx 확장자에 대해 System.Web.UI.SimpleHandlerFactory에 매핑되어 있기 때문입니다.

<httpHandlers>
 <add verb="*" path="*.ashx" type="System.Web.UI.SimpleHandlerFactory" />
</httpHandlers>

다음은 TestHandler.ashx의 예입니다.

============= HelloWorld.ashx =============
<%@ WebHandler Language="C#" CodeBehind="TestHandler.ashx.cs" Class="WebApplication1.TestHandler" %>

============= HelloWorld.ashx.cs =============
namespace WebApplication1
{
    public class TestHandler : IHttpHandler
    {
        public bool IsReusable
        {
            get { return true; }
        }

        public void ProcessRequest(HttpContext context)
        {
            System.Diagnostics.Trace.WriteLine("ProcessRequest");
        }
    }
}

.ashx로 등록된 HttpHandler의 재미있는 점은, IsReusable 값 설정이 전혀 의미가 없다는 것입니다. SimpleHandlerFactory 자체가 IsReusable 속성을 전혀 사용하지 않으며 기본값으로 false를 사용하기 때문입니다. 그렇다면, IsReusable 값이 의미가 있으려면 어떻게 해야 할까요?

바로 .ashx에 기대지 않고 순수한 "ASP.NET Handler"를 구현해 주어야 합니다.

more_detail_about_IHttpHandler_IsReusable_2.png

아래는 이를 구현한 코드입니다.

============= TestHandler2.cs =============
namespace WebApplication1
{
    public class TestHandler2 : IHttpHandler
    {
        public bool IsReusable
        {
            get { return true; }
        }

        public void ProcessRequest(HttpContext context)
        {
            System.Diagnostics.Trace.WriteLine("TestHandler2.ProcessRequest");
        }
    }
}

.ashx 구현과는 다르게 "CodeBehind" 형식이 아닌 단일 cs 파일로만 이루어져 있고, 이렇게 구현된 HttpHandler를 외부에서 호출할 수 있게 만들려면 반드시 web.config에 handler를 등록해 주어야 합니다.

<handlers>
    ...[생략]...
    <add name="testhandler" path="testhandler.axd" verb="GET" type="WebApplication1.TestHandler2" />
    ...[생략]...
</handlers>

바로 이렇게 등록된 HttpHandler만이 IsReusable 속성값이 불려져서 의미가 있게 됩니다.




그런데, IsReusable의 MSDN 문서 도움말 내용에 일부 개발자들이 혼란을 일으키고 있는 것 같습니다.

IHttpHandler.IsReusable Property
; https://docs.microsoft.com/en-us/dotnet/api/system.web.ihttphandler.isreusable#System_Web_IHttpHandler_IsReusable

단 한 줄로, 다음과 같이 설명되어 있습니다.

Gets a value indicating whether another request can use the IHttpHandler instance.


웹 검색을 해보면, IsReusable을 다음과 같이 해석하는 것을 볼 수 있습니다.

Explination of IsReusable
; http://forums.asp.net/t/851087.aspx/1?Explination+of+IsReusable

The IsReusable property is there for ASP.NET should recreate the HttpHandler every single request that comes through the ASP.NET engine. So if you set IsReusable to true, ASP.NET will only make one instance of the HttpHandler to be served throughout the application life cycle. If IsReusable is set to false, ASP.NET will create a new instance of the HttpHandler for each request that comes in. So in other words, if Reusable is set to true, it follows the singleton pattern[:)]


다음의 글도 마찬가지이고,

IsReusable on the IHttpHandler interface
; http://foreachbiscuit.wordpress.com/2007/11/01/isreusable-on-the-ihttphandler-interface/

The IsReusable property enables you to indicate that the same instance can be pooled and used by concurrent requests.
If you are indicating that the HttpHandler can be re-used then it needs to be programmed with thread safety in mind.


이들의 글처럼 과연 그럴까요?

테스트를 위해서 다음과 같이 간단하게 코드를 추가해 보면 됩니다.

namespace WebApplication1
{
    public class TestHandler2 : IHttpHandler
    {
        int count = 0;
        static int s_count = 0;

        public TestHandler2()
        {
            s_count++;
        }

        public bool IsReusable
        {
            get { return true; }
        }

        public void ProcessRequest(HttpContext context)
        {
            Trace.WriteLine("Instance Field : " + count);
            Trace.WriteLine("InstanceCount :" + s_count + ", Thread Id: " + Thread.CurrentThread.ManagedThreadId);
            count++;

            Thread.Sleep(5000);
        }
    }
}

그런 다음, 웹 브라우저를 2개 띄우고 TestHandler2 핸들러가 호출될 수 있도록 지정된 경로로 (5초 이내에) 동시에 방문합니다.

만약, 위에 나열된 링크들의 설명이 맞다면 각각 다음과 같은 결과가 나와야 합니다.

=== 첫 번째 요청 ===
Instance Field: 0
InstanceCount: 1, Thread Id: 8

=== 뒤이은 요청 ===
Instance Field: 1
InstanceCount: 1, Thread Id: 9

하지만, 실제 결과는 다음과 같이 출력됩니다.

=== 첫 번째 요청 ===
Instance Field: 0
InstanceCount: 1, Thread Id: 8

=== 뒤이은 요청 ===
Instance Field: 0
InstanceCount: 2, Thread Id: 9

해석이 되시죠? IsReusable == true인 경우, 해당 인스턴스는 객체 풀에 보관되며 다음의 요청이 들어왔을 때 객체 풀로부터 빠져나와서 서비스가 된 후 다시 객체 풀로 돌아가게 됩니다. 만약, 다른 쓰레드에서 동일한 요청이 들어오면 객체 풀을 살펴보고 보관된 핸들러 인스턴스가 없다면 새롭게 하나를 생성한 후 서비스를 하게 됩니다.

따라서, IsReusable == true인 핸들러는 Concurrent 요청으로부터 서비스 되지 않습니다. 즉, Thread-safe할 필요가 없으며 Singleton 인스턴스처럼 동작하지도 않습니다.

물론, static 필드가 있다면 thread-safe 하도록 만들어야 하지만 그것은 IsReusable == true에 상관없이, 모든 일반적인 타입이 static 필드를 가지고 있다면 고려해야 하는 것일 뿐 IsReusable == true라고 해서 요구되는 것은 아닙니다. (Singleton 형식이라면 instance field에까지 thread-safe가 되어야 합니다.)

다시, MSDN 문서의 도움말 설명을 보면 이런 상황들이 이해가 됩니다.

Gets a value indicating whether another request can use the IHttpHandler instance.


"another request"라는 의미가 "Concurrent requests can use the same IHttpHandler instance"로 해석되어서는 안됩니다.

첨부된 파일은 위의 코드를 포함한 예제 프로젝트입니다.




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







[최초 등록일: ]
[최종 수정일: 6/21/2021]

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

비밀번호

댓글 작성자
 




... [106]  107  108  109  110  111  112  113  114  115  116  117  118  119  120  ...
NoWriterDateCnt.TitleFile(s)
11274정성태8/22/201719391.NET Framework: 674. Thread 타입의 Suspend/Resume/Join 사용 관련 예외 처리
11273정성태8/22/201721634오류 유형: 415. 윈도우 업데이트 에러 Error 0x80070643
11272정성태8/21/201724804VS.NET IDE: 120. 비주얼 스튜디오 2017 버전 15.3.1 - C# 7.1 공개 [2]
11271정성태8/19/201719263VS.NET IDE: 119. Visual Studio 2017에서 .NET Core 2.0 프로젝트 환경 구성하는 방법
11270정성태8/17/201730725.NET Framework: 673. C#에서 enum을 boxing 없이 int로 변환하기 [2]
11269정성태8/17/201721495디버깅 기술: 93. windbg - 풀 덤프에서 .NET 스레드의 상태를 알아내는 방법
11268정성태8/14/201721083디버깅 기술: 92. windbg - C# Monitor Lock을 획득하고 있는 스레드 찾는 방법
11267정성태8/10/201725138.NET Framework: 672. 모노 개발 환경
11266정성태8/10/201724946.NET Framework: 671. C# 6.0 이상의 소스 코드를 Visual Studio 설치 없이 명령행에서 컴파일하는 방법
11265정성태8/10/201753187기타: 66. 도서: 시작하세요! C# 7.1 프로그래밍: 기본 문법부터 실전 예제까지 [11]
11264정성태8/9/201724113오류 유형: 414. UWP app을 signtool.exe로 서명 시 0x8007000b 오류 발생
11263정성태8/9/201719597오류 유형: 413. The C# project "..." is targeting ".NETFramework, Version=v4.0", which is not installed on this machine. [3]
11262정성태8/5/201718256오류 유형: 412. windbg - SOS does not support the current target architecture. [3]
11261정성태8/4/201720834디버깅 기술: 91. windbg - 풀 덤프 파일로부터 강력한 이름의 어셈블리 추출 후 사용하는 방법
11260정성태8/3/201718953.NET Framework: 670. C# - 실행 파일로부터 공개키를 추출하는 방법
11259정성태8/2/201718190.NET Framework: 669. 지연 서명된 어셈블리를 sn.exe -Vr 등록 없이 사용하는 방법
11258정성태8/1/201719021.NET Framework: 668. 지연 서명된 DLL과 서명된 DLL의 차이점파일 다운로드1
11257정성태7/31/201719175.NET Framework: 667. bypassTrustedAppStrongNames 옵션 설명파일 다운로드1
11256정성태7/25/201720663디버깅 기술: 90. windbg의 lm 명령으로 보이지 않는 .NET 4.0 ClassLibrary를 명시적으로 로드하는 방법 [1]
11255정성태7/18/201723217디버깅 기술: 89. Win32 Debug CRT Heap Internals의 0xBAADF00D 표시 재현 [1]파일 다운로드3
11254정성태7/17/201719594개발 환경 구성: 322. "Visual Studio Emulator for Android" 에뮬레이터를 "Android Studio"와 함께 쓰는 방법
11253정성태7/17/201719950Math: 21. "Coding the Matrix" 문제 2.5.1 풀이 [1]파일 다운로드1
11252정성태7/13/201718462오류 유형: 411. RTVS 또는 PTVS 실행 시 Could not load type 'Microsoft.VisualStudio.InteractiveWindow.Shell.IVsInteractiveWindowFactory2'
11251정성태7/13/201717166디버깅 기술: 88. windbg 분석 - webengine4.dll의 MgdExplicitFlush에서 발생한 System.AccessViolationException의 crash 문제 (2)
11250정성태7/13/201720722디버깅 기술: 87. windbg 분석 - webengine4.dll의 MgdExplicitFlush에서 발생한 System.AccessViolationException의 crash 문제 [1]
11249정성태7/12/201718548오류 유형: 410. LoadLibrary("[...].dll") failed - The specified procedure could not be found.
... [106]  107  108  109  110  111  112  113  114  115  116  117  118  119  120  ...