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

비밀번호

댓글 작성자
 




... 46  47  48  49  [50]  51  52  53  54  55  56  57  58  59  60  ...
NoWriterDateCnt.TitleFile(s)
12721정성태7/20/202121071오류 유형: 738. The trust relationship between this workstation and the primary domain failed. - 세 번째 이야기
12720정성태7/19/202116385Linux: 43. .NET Core/5+ 응용 프로그램의 Ubuntu (Debian) 패키지 준비
12719정성태7/19/202115375오류 유형: 737. SharePoint 설치 시 "0x800710D8 The object identifier does not represent a valid object." 오류 발생
12718정성태7/19/202114999개발 환경 구성: 581. Windows에서 WSL로 파일 복사 시 root 소유권으로 적용되는 문제파일 다운로드1
12717정성태7/18/202115582Windows: 195. robocopy에서 파일의 ADS(Alternate Data Stream) 정보 복사를 제외하는 방법
12716정성태7/17/202116030개발 환경 구성: 580. msbuild의 Exec Task에 robocopy를 사용하는 방법파일 다운로드1
12715정성태7/17/202123464오류 유형: 736. Windows - MySQL zip 파일 버전의 "mysqld --skip-grant-tables" 실행 시 비정상 종료 [1]
12714정성태7/16/202116631오류 유형: 735. VCRUNTIME140.dll, MSVCP140.dll, VCRUNTIME140.dll, VCRUNTIME140_1.dll이 없어 exe 실행이 안 되는 경우
12713정성태7/16/202118807.NET Framework: 1077. C# - 동기 방식이면서 비동기 규약을 따르게 만드는 Task.FromResult파일 다운로드1
12712정성태7/15/202117406개발 환경 구성: 579. Azure - 리눅스 호스팅의 Site Extension 제작 방법
12711정성태7/15/202116885개발 환경 구성: 578. Azure - Java Web App Service를 위한 Site Extension 제작 방법
12710정성태7/15/202120217개발 환경 구성: 577. MQTT - emqx.io 서비스 소개
12709정성태7/14/202115742Linux: 42. 실행 중인 docker 컨테이너에 대한 구동 시점의 docker run 명령어를 확인하는 방법
12708정성태7/14/202120116Linux: 41. 리눅스 환경에서 디스크 용량 부족 시 원인 분석 방법
12707정성태7/14/202186669오류 유형: 734. MySQL - Authentication method 'caching_sha2_password' not supported by any of the available plugins.
12706정성태7/14/202118058.NET Framework: 1076. C# - AsyncLocal 기능을 CallContext만으로 구현하는 방법 [2]파일 다운로드1
12705정성태7/13/202118848VS.NET IDE: 168. x64 DLL 프로젝트의 컨트롤이 Visual Studio의 Designer에서 보이지 않는 문제 - 두 번째 이야기
12704정성태7/12/202117382개발 환경 구성: 576. Azure VM의 서비스를 Azure Web App Service에서만 접근하도록 NSG 설정을 제한하는 방법
12703정성태7/11/202122377개발 환경 구성: 575. Azure VM에 (ICMP) ping을 허용하는 방법
12702정성태7/11/202118643오류 유형: 733. TaskScheduler에 등록된 wacs.exe의 Let's Encrypt 인증서 업데이트 문제
12701정성태7/9/202118216.NET Framework: 1075. C# - ThreadPool의 스레드는 반환 시 ThreadStatic과 AsyncLocal 값이 초기화 될까요?파일 다운로드1
12700정성태7/8/202118702.NET Framework: 1074. RuntimeType의 메모리 누수? [1]
12699정성태7/8/202117344VS.NET IDE: 167. Visual Studio 디버깅 중 GC Heap 상태를 보여주는 "Show Diagnostic Tools" 메뉴 사용법
12698정성태7/7/202121423오류 유형: 732. Windows 11 업데이트 시 3% 또는 0%에서 다운로드가 멈춘 경우
12697정성태7/7/202116025개발 환경 구성: 574. Windows 11 (Insider Preview) 설치하는 방법
12696정성태7/6/202117408VC++: 146. 운영체제의 스레드 문맥 교환(Context Switch)을 유사하게 구현하는 방법파일 다운로드2
... 46  47  48  49  [50]  51  52  53  54  55  56  57  58  59  60  ...