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

비밀번호

댓글 작성자
 




1  [2]  3  4  5  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13893정성태2/27/20252244Linux: 115. eBPF (bpf2go) - ARRAY / HASH map 기본 사용법
13892정성태2/24/20253005닷넷: 2325. C# - PowerShell과 연동하는 방법파일 다운로드1
13891정성태2/23/20252513닷넷: 2324. C# - 프로세스의 성능 카운터용 인스턴스 이름을 구하는 방법파일 다운로드1
13890정성태2/21/20252344닷넷: 2323. C# - 프로세스 메모리 중 Private Working Set 크기를 구하는 방법(Win32 API)파일 다운로드1
13889정성태2/20/20253114닷넷: 2322. C# - 프로세스 메모리 중 Private Working Set 크기를 구하는 방법(성능 카운터, WMI) [1]파일 다운로드1
13888정성태2/17/20252520닷넷: 2321. Blazor에서 발생할 수 있는 async void 메서드의 부작용
13887정성태2/17/20253100닷넷: 2320. Blazor의 razor 페이지에서 code-behind 파일로 코드를 분리 및 DI 사용법
13886정성태2/15/20252577VS.NET IDE: 196. Visual Studio - Code-behind처럼 cs 파일을 그룹핑하는 방법
13885정성태2/14/20253261닷넷: 2319. ASP.NET Core Web API / Razor 페이지에서 발생할 수 있는 async void 메서드의 부작용
13884정성태2/13/20253547닷넷: 2318. C# - (async Task가 아닌) async void 사용 시의 부작용파일 다운로드1
13883정성태2/12/20253295닷넷: 2317. C# - Memory Mapped I/O를 이용한 PCI Configuration Space 정보 열람파일 다운로드1
13882정성태2/10/20252591스크립트: 70. 파이썬 - oracledb 패키지 연동 시 Thin / Thick 모드
13881정성태2/7/20252841닷넷: 2316. C# - Port I/O를 이용한 PCI Configuration Space 정보 열람파일 다운로드1
13880정성태2/5/20253185오류 유형: 947. sshd - Failed to start OpenSSH server daemon.
13879정성태2/5/20253428오류 유형: 946. Ubuntu - N: Updating from such a repository can't be done securely, and is therefore disabled by default.
13878정성태2/3/20253213오류 유형: 945. Windows - 최대 절전 모드 시 DRIVER_POWER_STATE_FAILURE 발생 (pacer.sys)
13877정성태1/25/20253266닷넷: 2315. C# - PCI 장치 열거 (레지스트리, SetupAPI)파일 다운로드1
13876정성태1/25/20253727닷넷: 2314. C# - ProcessStartInfo 타입의 Arguments와 ArgumentList파일 다운로드1
13875정성태1/24/20253168스크립트: 69. 파이썬 - multiprocessing 패키지의 spawn 모드로 동작하는 uvicorn의 workers
13874정성태1/24/20253578스크립트: 68. 파이썬 - multiprocessing Pool의 기본 프로세스 시작 모드(spawn, fork)
13873정성태1/23/20252998디버깅 기술: 217. WinDbg - PCI 장치 열거파일 다운로드1
13872정성태1/23/20252907오류 유형: 944. WinDbg - 원격 커널 디버깅이 연결은 되지만 Break (Ctrl + Break) 키를 눌러도 멈추지 않는 현상
13871정성태1/22/20253304Windows: 278. Windows - 윈도우를 다른 모니터 화면으로 이동시키는 단축키 (Window + Shift + 화살표)
13870정성태1/18/20253758개발 환경 구성: 741. WinDbg - 네트워크 커널 디버깅이 가능한 NIC 카드 지원 확대
13869정성태1/18/20253477개발 환경 구성: 740. WinDbg - _NT_SYMBOL_PATH 환경 변수에 설정한 경로로 심벌 파일을 다운로드하지 않는 경우
13868정성태1/17/20253121Windows: 277. Hyper-V - Windows 11 VM의 Enhanced Session 모드로 로그인을 할 수 없는 문제
1  [2]  3  4  5  6  7  8  9  10  11  12  13  14  15  ...