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

비밀번호

댓글 작성자
 




... 121  122  123  124  125  126  127  128  129  130  131  132  [133]  134  135  ...
NoWriterDateCnt.TitleFile(s)
1730정성태8/11/201422125개발 환경 구성: 234. Royal TS의 터미널(Terminal) 연결에서 한글이 깨지는 현상 해결 방법
1729정성태8/11/201418195오류 유형: 236. SqlConnection - The requested Performance Counter is not a custom counter, it has to be initialized as ReadOnly.
1728정성태8/8/201430238.NET Framework: 453. C# - 오피스 파워포인트(Powerpoint) 파일을 WinForm에서 보는 방법파일 다운로드1
1727정성태8/6/201420474오류 유형: 235. SignalR 오류 메시지 - Counter 'Messages Bus Messages Published Total' does not exist in the specified Category. [2]
1726정성태8/6/201419385오류 유형: 234. IIS Express에서 COM+ 사용 시 SecurityException - "Requested registry access is not allowed" 발생
1725정성태8/6/201421328오류 유형: 233. Visual Studio 2013 Update3 적용 후 Microsoft.VisualStudio.Web.PageInspector.Runtime 모듈에 대한 FileNotFoundException 예외 발생
1724정성태8/5/201426093.NET Framework: 452. .NET System.Threading.Thread 개체에서 Native Thread Id를 구하는 방법 - 두 번째 이야기 [1]파일 다운로드1
1723정성태7/29/201458311개발 환경 구성: 233. DirectX 9 예제 프로젝트 빌드하는 방법 [3]파일 다운로드1
1722정성태7/25/201421024오류 유형: 232. IIS 500 Internal Server Error - NTFS 암호화된 폴더에 웹 애플리케이션이 위치한 경우
1721정성태7/24/201424035.NET Framework: 451. 함수형 프로그래밍 개념 - 리스트 해석(List Comprehension)과 순수 함수 [2]
1720정성태7/23/201422067개발 환경 구성: 232. C:\WINDOWS\system32\LogFiles\HTTPERR 폴더에 로그 파일을 남기지 않는 설정
1719정성태7/22/201426000Math: 13. 동전을 여러 더미로 나누는 경우의 수 세기(Partition Number) - 두 번째 이야기파일 다운로드1
1718정성태7/19/201435262Math: 12. HTML에서 수학 관련 기호/수식을 표현하기 위한 방법 - MathJax.js [4]
1716정성태7/17/201434977개발 환경 구성: 231. PC 용 무료 안드로이드 에뮬레이터 - genymotion
1715정성태7/13/201430582기타: 47. 운영체제 종료 후에도 USB 외장 하드의 전원이 꺼지지 않는 경우 [3]
1714정성태7/11/201420887VS.NET IDE: 92. Visual Studio 2013을 지원하는 IL Support 확장 도구
1713정성태7/11/201444585Windows: 98. 윈도우 시스템 디스크 용량 확보를 위한 "Package Cache" 폴더 이동 [1]
1712정성태7/10/201432856.NET Framework: 450. 영문 윈도우에서 C# 콘솔 프로그램의 유니코드 출력 방법 [3]
1711정성태7/10/201438042Windows: 97. cmd.exe 창에서 사용할 폰트를 추가하는 방법 [1]
1710정성태7/8/201430572개발 환경 구성: 230. 유니코드의 Surrogate Pair, Supplementary Characters가 뭘까요?파일 다운로드2
1709정성태7/8/201427336VS.NET IDE: 91. Visual Studio에서 32/64비트 IIS Express 실행하는 방법
1708정성태7/7/201424698VS.NET IDE: 90. Visual Studio - 사용자 정의 정적 분석 규칙 만드는 방법 [3]파일 다운로드1
1707정성태7/4/201422979.NET Framework: 449. C#에서 C++로 VARIANT 넘겨주는 방법파일 다운로드1
1706정성태7/3/201421389.NET Framework: 448. .NET SmartClient 컨트롤을 윈도우 8/2012에서 활성화하는 방법파일 다운로드1
1705정성태7/2/201435037VC++: 78. 보이어-무어(Boyer-Moore) 알고리즘이 정말 빠를까? [6]파일 다운로드1
1704정성태7/2/201421631.NET Framework: 447. w3wp.exe AppPool 재생(recycle)하는 방법 정리
... 121  122  123  124  125  126  127  128  129  130  131  132  [133]  134  135  ...