Microsoft MVP성태의 닷넷 이야기
.NET Framework: 239. IHttpHandler.IsReusable 속성 이야기 [링크 복사], [링크+제목 복사],
조회: 27530
글쓴 사람
정성태 (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

비밀번호

댓글 작성자
 




... 31  32  33  34  35  36  37  38  39  40  41  42  43  44  [45]  ...
NoWriterDateCnt.TitleFile(s)
12814정성태8/30/202120198.NET Framework: 1110. C# 11 - 인터페이스 내에 정적 추상 메서드 정의 가능 (DIM for Static Members) [2]파일 다운로드1
12813정성태8/29/202117281.NET Framework: 1109. C# 10 - (11) Lambda 개선파일 다운로드1
12812정성태8/28/202116625.NET Framework: 1108. C# 10 - (10) 개선된 #line 지시자
12811정성태8/27/202116502Linux: 44. 윈도우 개발자를 위한 리눅스 fork 동작 방식 설명 (파이썬 코드)
12810정성태8/27/202115538.NET Framework: 1107. .NET Core/5+에서 동적 컴파일한 C# 코드를 (Breakpoint도 활용하며) 디버깅하는 방법 - #line 지시자파일 다운로드1
12809정성태8/26/202115481.NET Framework: 1106. .NET Core/5+에서 C# 코드를 동적으로 컴파일/사용하는 방법 [1]파일 다운로드1
12808정성태8/25/202117129오류 유형: 758. go: ...: missing go.sum entry; to add it: go mod download ...
12807정성태8/25/202117899.NET Framework: 1105. C# 10 - (9) 비동기 메서드가 사용할 AsyncMethodBuilder 선택 가능파일 다운로드1
12806정성태8/24/202114449개발 환경 구성: 601. PyCharm - 다중 프로세스 디버깅 방법
12805정성태8/24/202116151.NET Framework: 1104. C# 10 - (8) 분해 구문에서 기존 변수의 재사용 가능파일 다운로드1
12804정성태8/24/202116319.NET Framework: 1103. C# 10 - (7) Source Generator V2 APIs
12803정성태8/23/202116811개발 환경 구성: 600. pip cache 디렉터리 옮기는 방법
12802정성태8/23/202117248.NET Framework: 1102. .NET Conf Mini 21.08 - WinUI 3 따라해 보기 [1]
12801정성태8/23/202116839.NET Framework: 1101. C# 10 - (6) record class 타입의 ToString 메서드를 sealed 처리 허용파일 다운로드1
12800정성태8/22/202117212개발 환경 구성: 599. PyCharm - (반대로) 원격 프로세스가 PyCharm에 디버그 연결하는 방법
12799정성태8/22/202117554.NET Framework: 1100. C# 10 - (5) 속성 패턴의 개선파일 다운로드1
12798정성태8/21/202118860개발 환경 구성: 598. PyCharm - 원격 프로세스를 디버그하는 방법
12797정성태8/21/202116220Windows: 197. TCP의 MSS(Maximum Segment Size) 크기는 고정된 것일까요?
12796정성태8/21/202117221.NET Framework: 1099. C# 10 - (4) 상수 문자열에 포맷 식 사용 가능파일 다운로드1
12795정성태8/20/202117559.NET Framework: 1098. .NET 6에 포함된 신규 BCL API - 스레드 관련
12794정성태8/20/202116914스크립트: 23. 파이썬 - WSGI를 만족하는 최소한의 구현 코드 및 PyCharm에서의 디버깅 방법 [1]
12793정성태8/20/202117703.NET Framework: 1097. C# 10 - (3) 개선된 변수 초기화 판정파일 다운로드1
12792정성태8/19/202118857.NET Framework: 1096. C# 10 - (2) 전역 네임스페이스 선언파일 다운로드1
12791정성태8/19/202115651.NET Framework: 1095. C# COM 개체를 C++에서 사용하는 예제 [3]파일 다운로드1
12790정성태8/18/202119578.NET Framework: 1094. C# 10 - (1) 구조체를 생성하는 record struct파일 다운로드1
12789정성태8/18/202118234개발 환경 구성: 597. PyCharm - 윈도우 환경에서 WSL을 이용해 파이썬 앱 개발/디버깅하는 방법
... 31  32  33  34  35  36  37  38  39  40  41  42  43  44  [45]  ...