HTTP Compression Module

ASP.NET 2007. 3. 28. 17:28
안녕하세요... 요전에 포스트한 HTTP 압축에 관한 글에서 IIS 6.0의 HTTP 압축 설정 방법을 소개하면서 파일 확장자 로 압축하는 IIS 6.0의 단점을 지적한 바 있습니다. 그래서 함 만들어 봤습니다. HTTP 압축을 수행하는 HTTP 모듈(module)로서 파일 확장자가 아닌 Content-Type에 의해 압축을 수행하며, 작은 파일 크기는 압축을 해봤자 오히려 손해이므로 일정 크기를 넘어가면 압축을 수행하도록 구성했습니다. 소스 코드는 아직 공개할 단계가 아닌 것 같고, DLL 만을 공개하고자 합니다. 필요하신 분들은 다운로드 받아 사용해 보시고 피드백 주십시요.

HTTP Compression Module

IIS 6.0은 HTTP 압축 기능을 공짜로(요거이 중요하다) 지원하며 그 능력 역시 나쁘지 않은 것으로 평가되고 있다. 하지만 IIS 6.0의 압축 기능은 설정이 용이하지 않고 파일 확장자에 의거하여 압축을 수행한다는 단점이 있다. 또한 IIS 6.0을 직접 설정할 수 없는 사용자(호스팅으로 세들어 사는 불쌍한 세입자들.... 제가 그렇답니당...)는 IIS 6.0의 압축 기능은 그림의 떡이다. 요런 단점을 보완하고자 HTTP 압축을 수행하는 ASP.NET 모듈을 만드러 보았다. 단꿀과 물엿, 버터와 쇼트닝유가 흐르는 압축의 세계에 빠져 볼 것인가 ? 아니면 귀차니즘 젖어 느리게 뜨는 사이트의 응답속도에 "우리 사이트가 아니네?" 하고 쌩깔 것인가?

Http Compress Module Download Now

주) HttpCompress 모듈의 저작권은 필자에게 있으며 어떠한 보증도 포함하고 있지 않습니다. 이 모듈을 개인 사이트가 아닌 상업적 사이트나 커뮤니티 사이트에 사용하고자 하는 경우, 필자의 허락을 얻어야 합니다. 또한 이 소프트웨어/모듈을 재 배포하고자 하는 경우에도 필자의 허락을 얻어야 합니다.

Implementation Overview

HTTP 모듈이라 함은 ASP.NET의 기본 처리 과정에서 HTTP Request가 처리 되기 전에 수행되는 필터 혹은 전처리/후처리를 수행할 수 있는 ASP.NET의 인프라이다. ASP.NET이 제공하는 Cookie-less 세션 기능, Form 인증 등이 모두 HTTP 모듈을 통해 구현되고 있다. HTTP 모듈은 다양한 방면에서 유용하게 써먹을 수 있으므로 다음에 상세히 이빨을 까기로 하고, 여기서는 필자가 작성한 HTTP 압축 기능이 ASP.NET의 HTTP 모듈로 작성되었다는 것만 알아 두면 되긋다.

HTTP 모듈은 System.Web.IHttpModule 인터페이스를 구현하는 클래스이다. 필자의 HttpCompress.CompressModule 클래스는 IHttpModule 인터페이스를 구현하는 클래스로서 HTTP Request가 수행될 때마다 수행되는 BeginRequest 이벤트에서 압축 필터를 Request에 설치한다. 필터라 함은 HttpResponse 클래스의 Filter 속성에 Stream 객체를 할당하는 작업으로써, HttpResponse 객체에 쓰여지는 모든 데이터가 Filter 속성에 지정된 스트림을 통하게 된다는 것이다. 따라서 이 Filter 속성에 압축을 수행하는 Stream 객체를 할당하면 압축이 수행되게 되는 것이다. (원리는 간단하다... -_-) 대략 귀차니즘 때문에 상세한 코드 설명은 생략... (난중에 소스 공개할 때 같이 설명하거나 HTTP 모듈을 통한 필터링을 꼭지로 별도 포스트를 해볼까 한다... 기다리진 마라... 필자가 워낙 게을러서리... 아~흐~)

Installation

압축 파일을 디비보면 달랑 HttpCompress.dll 하나만 나온다. 요 DLL을 웹 어플리케이션의 bin 폴더에 복사한다. 그리고 web.config에 HTTP 모듈에 관련된 설정을 다음과 같이 집어 넣는다.

<?xmlversion="1.0"encoding="utf-8"?>
<configuration>
<appSettings>
<!--압축 설정 :
Content-Type과 압축할 Http Response의 크기를 설정한다.
다음 예제 설정은 Content-Type이 text/html 과 text/xml이며 그 크기가 8KB 이상 인 경우만
압축을 수행한다.
-->

<addkey="CompressContentTypes"value="text/html:text/xml"/>
<
addkey="CompressThreshold"value="8KB"/>
</
appSettings>
<system.web>
<httpModules>
<addname="HttpCompress"type="HttpCompress.CompressModule,HttpCompress"/>
</
httpModules>
<!--이하 다른 부분 생략-->
</system.web>
</configuration>

먼저 <system.web> 요소의 <httpModules> 요소에 HTTP 모듈을 설정을 추가한다. 위 예제는 HttpCompress.dll 을 bin 디렉토리에 복사하는 경우의 예제이며, HttpCompress.dll을 GAC에 추가할 때는 HttpCompress 어셈블리의 버전, publictoken 등(어셈블리 Strong Name:: HttpCompress, Version=0.9.0.0, Culture=neutral, PublicKeyToken=9dc9c6c30d305b4e)을 명시해 주어야 한다.

다음은 <appSettings>에 HttpCompress 모듈이 압축을 수행할 조건을 명시해야 한다. CompressContentType 은 압축을 수행할 Content-Type을 명시한다. 위의 예에서는 일반 HTML 문서의 Content-Type과 XML 문서(웹 서비스)의 Content-Type인 text/html 과 text/xml 인 경우에만 압축을 수행하도록 설정하고 있다. 아직은 * 와 같은 와일드 카드는 지원하지 않는다. -_-

CompressThreshold 설정은 여기서 주어진 크기 이상의 Response 일 때만 압축을 수행하도록 설정한다. 필자의 테스트상 작은 크기의 컨텐츠를 압축하면 압축하는데 소요되는 오버헤드가 네트워크 상에서 얻는 잇점보다 커져서 오히려 성능에 도움이 되지 않았기 때문이다. 따라서 8-12KB 정도로 설정해 두면, 이 보다 큰 크기의 컨텐츠만이 압축되게 된다. 이 설정에 대한 절대적으로 적절한 값은 없다. 웹 어플리케이션과 사이트마다 웹 서버의 CPU 개수, 기타 네트워크 상황등을 고려하여 설정해 주어야 한다. 네트워크 사정이 상대적으로 좋은 환경이라면 이 값을 크게 잡는 것이 좋고 네트워크 사정이 좋지 못하다면 이 값을 낮게 잡아주는 것이 좋다.

이렇게 설정하면 설치가 완료된다. (이 얼마나 간단한가 !!! 아니면 말고... -_-) 이제 웹 어플리케이션이 재시작되면 (web.config를 수정했으니 재 시작될 것이다) Fiddler나 기타 다른 방법을 통해 컨텐츠가 압축되는가 확인해 보자.

Consideration

필자가 작성한 HTTP 압축 모듈은 ASP.NET의 HTTP 모듈로서 작성하였다고 하였다. 여기에 많은 함축적인 의미가 포함된다. 첫째로, 이 모듈은 오직 ASP.NET에 의해 처리되는 컨텐츠 만을 압축한다. 따라서 ASP.NET에 의해 처리되지 않는 PHP, ASP 나 정적 컨텐츠들(htm, xml 파일 등)은 비록 Content-Type이 설정에서 명시되어 있더라도 압축되지 않는다. ASP와 같은 비 닷넷 스크립트를 압축하고자 한다면 HTTP 모듈이 아닌 ISAPI 필터로 압축을 구현해야 함을 기억하자. (요거 구현이 절라 빡시다. 필요하면 돈주고 사라. 별로 비싸지도 않드라...)

둘째로, ASP.NET의 HTTP 모듈은 웹 어플리케이션 별로 적용된다. 따라서 웹 어플리케이션에 필자의 HttpCompress 모듈이 설치되면 웹 어플리케이션의 모든 컨텐츠에 대해 압축이 적용된다. 즉, 디렉토리 별로 압축 여부를 설정할 수 없다. 물론 HttpCompress 모듈이 디렉토리별로 설정을 읽어 압축 여부를 결정하도록 구현할 수도 있다. 아쉽게도 필자에게 아직 이 부분을 구현할 시간적 여유가 없었다.

세째로, 필자의 HTTP 모듈은 ASP.NET의 Output Cache를 지원한다. (뭐 사실 지원하고 싶어서 지원한건 아니고... 걍 만들다 보니 지원이 되었다. -_-) 필자가 테스트 한 결과 Output Cache가 설정된 aspx, asmx 에 대해 압축이 수행되었고, 압축된 컨텐츠는 Output Cache에 의해 훌륭하게(?) 캐시가 된다.

Recommendation

필자의 압축 모듈이 유용하다고 판단되어 실제로 적용하고자 한다면, 필자의 다음 권고를 고려해 보기 바란다.

첫째, 웹 사이트가 정적 컨텐츠 보다 동적 컨텐츠(.aspx, .ascx 등)가 더 많은 경우, 압축 모듈은 효과를 볼 수 있다.

둘째, 웹 브라우저를 수행하는 최종 사용자까지의 네트워크 회선이 10Mbps 정도로 초고속(100Mbps)이 아닌 경우 압축은 효과를 본다. 모든 사용자가 100Mbps 이상의 대역폭을 갖는다면 압축의 효과는 미비하거나 역 효과가 나타날 수도 있다. 하지만 다수의 사용자가 10Mbps 이하의 대역폭을 사용한다면 압축은 효과를 볼 것이다.

Update 2007-01-12
100Mbps 의 초고속 네트워크를 사용하더라도 다수의 사용자에 의해 네트워크 회선이 병목을 일으킨다면 콘텐트 압축은 충분한 효과를 낸다.

셋째, 정적 컨텐츠의 압축은 필자의 압축 모듈에 의해 압축되지 않기 때문에 IIS 6.0의 정적 컨텐츠 압축을 이용하는 것이 좋다. IIS 6.0의 정적 컨텐츠 압축은 압축된 파일을 캐시하므로 효율이 매우 좋다. 어떤 압축 기법(제품, 모듈...)을 사용하더라도 정적 컨텐츠의 압축은 IIS 6.0이 효율적으로 수행해 준다.

넷째, 동적 컨텐츠가 작은 조각들로 구성된 경우, 압축의 효과는 미비하다. 예를 들어, 다수의 IFRAME과 FRAME으로 구성된 웹 페이지는 상대적으로 작은 컨텐츠들이 여러 번 서비스되므로, HTTP 압축이 각각 작은 컨텐츠를 압축하게 되어 성능 상의 잇점을 많이 누리기 어렵다. 반면 단일 페이지가 100KB 이상의 HTML 을 렌더링 한다면 압축 모듈의 효과는 크다.

다섯째, 웹 서버의 CPU 사용률이 높은 편이라면 압축을 사용하지 않거나, 굳이 사용해야 한다면 웹 서버의 CPU를 더 추가해 주거나(Scale-up) 웹 서버를 추가(Scale-Out)해야 한다. HTTP 압축 모듈을 사용함으로써 더 많은 CPU 자원이 소요되기 때문에, 이미 CPU 사용률을 높은 웹 서버는 추가의 부하를 감당할 수 없을 수도 있다. 이러한 경우에는 압축이 오히려 응답 속도를 느리게 하는 요인이 될 수 있다.

FeedBack

필자의 압축 모듈에 대한 버그 리포트나 기능 추가 사항들이 있다면 이 글에 피드백을 남기거나 메일 주기 바란다. 가능한 시간을 내어 수정하도록 하겠다. 또한 이 모듈을 사용하여 효과를 보았다면 필자에게 꼭 연락 주기 바란다.

경고 : 이 글을 무단으로 복제/스크랩하여 타 게시판, 블로그(개인 블로그 포함)에 게시하는 것은 허용하지 않습니다.
FeedBack
#re: HTTP 압축 모듈 구현 !!! 함 써 보셈~ 2005-08-30 오후 9:02 정준명
'/' 응용 프로그램에 서버 오류가 있습니다.
--------------------------------------------------------------------------------

파일이나 어셈블리 이름 ICSharpCode.SharpZipLib 또는 여기에 종속되어 있는 파일이나 어셈블리 중 하나를 찾을 수 없습니다.
설명: 현재 웹 요청을 실행하는 동안 처리되지 않은 예외가 발생했습니다. 스택 추적을 검토하여 발생한 오류 및 코드에서 오류가 발생한 위치에 대한 자세한 정보를 확인하십시오.

예외 정보: System.IO.FileNotFoundException: 파일이나 어셈블리 이름 ICSharpCode.SharpZipLib 또는 여기에 종속되어 있는 파일이나 어셈블리 중 하나를 찾을 수 없습니다.

소스 오류:

---
이런 오류가 나네요. ICSharpCode.SharpZipLib 요게 몰까요?

#re: HTTP 압축 모듈 구현 !!! 함 써 보셈~ 2005-08-30 오후 10:39 블로그 쥔장
그건... 닷넷 압축 라이브러리 입니다. #ZipLib(SharpZipLib 로 발음) 라이브러리라는 것인데...
제가 재배포 권한이 있는지 아리까리 해서 포함시키지 않았습니다.
다운로드 URL은

http://www.icsharpcode.net/OpenSource/SharpZipLib/Default.aspx

입니다.
#re: HTTP 압축 모듈 구현 !!! 함 써 보셈~ 2007-02-26 오전 11:14 임대열
안녕하세요. 제가 테스트 해보려고 하는데 다음과 같은 에러가 발생합니다.

'ICSharpCode.SharpZipLib' 의 버전이 안맞아서 그런것 같습니다.

어떻게 해야 할까요? ^^;


이름이 'ICSharpCode.SharpZipLib'인 찾은 어셈블리의 매니페스트 정의가 어셈블리 참조와 일치하지 않습니다.

설명: 현재 웹 요청을 실행하는 동안 처리되지 않은 예외가 발생했습니다. 스택 추적을 검토하여 발생한 오류 및 코드에서 오류가 발생한 위치에 대한 자세한 정보를 확인하십시오.

예외 정보: System.IO.FileLoadException: 이름이 'ICSharpCode.SharpZipLib'인 찾은 어셈블리의 매니페스트 정의가 어셈블리 참조와 일치하지 않습니다.

소스 오류:

현재 웹 요청을 실행하는 동안 처리되지 않은 예외가 생성되었습니다. 아래의 예외 스택 추적을 사용하여 예외의 원인 및 위치 정보를 확인할 수 있습니다.

어셈블리 로드 추적: 다음 정보는 'ICSharpCode.SharpZipLib' 어셈블리를 로드할 수 없는 이유를 알아내는 데 도움이 됩니다.


=== Pre-bind state information ===
LOG: DisplayName = ICSharpCode.SharpZipLib, Version=0.83.1.0, Culture=neutral, PublicKeyToken=1b03e6acf1164f73
(Fully-specified)
LOG: Appbase = file:///D:/WebService
LOG: Initial PrivatePath = bin
Calling assembly : (Unknown).
===

LOG: Publisher policy file is not found.
LOG: No redirect found in host configuration file (C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\aspnet.config).
LOG: Using machine configuration file from C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\config\machine.config.
LOG: Post-policy reference: ICSharpCode.SharpZipLib, Version=0.83.1.0, Culture=neutral, PublicKeyToken=1b03e6acf1164f73
LOG: Attempting download of new URL file:///C:/WINDOWS/Microsoft.NET/Framework/v1.1.4322/Temporary ASP.NET Files/testwebservice/df801124/6a14eb1e/ICSharpCode.SharpZipLib.DLL.
LOG: Attempting download of new URL file:///C:/WINDOWS/Microsoft.NET/Framework/v1.1.4322/Temporary ASP.NET Files/testwebservice/df801124/6a14eb1e/ICSharpCode.SharpZipLib/ICSharpCode.SharpZipLib.DLL.
LOG: Attempting download of new URL file:///D:/WebService/bin/ICSharpCode.SharpZipLib.DLL.
WRN: Comparing the assembly name resulted in the mismatch: Minor Version



스택 추적:


[FileLoadException: 이름이 'ICSharpCode.SharpZipLib'인 찾은 어셈블리의 매니페스트 정의가 어셈블리 참조와 일치하지 않습니다.]
HttpCompress.b..ctor(Stream targetStream, Int32 upperBound) +0
HttpCompress.CompressModule.a(Object A_0, EventArgs A_1) +165
System.Web.SyncEventExecutionStep.System.Web.HttpApplication+IExecutionStep.Execute() +60
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +87




--------------------------------------------------------------------------------
버전 정보: Microsoft .NET Framework 버전:1.1.4322.2300; ASP.NET 버전:1.1.4322.2300
#re: HTTP 압축 모듈 구현 !!! 함 써 보셈~ 2007-02-26 오후 2:43 블로그쥔장
제가 작성한 모듈이 이전 버전의 SharpZipLib를 참조하고 있어서 발생하는 오류 입니다.
이를 해결하는 방법은 최신 버전을 참조하도록 압축 모듈을 재 컴파일(사실상 어렵군요.. -_-)하거나
bindingRedirection을 이용하여 구버전 대신 신버전을 바인딩하도록 configuration에 설정하는 겁니다.

http://msdn2.microsoft.com/en-us/library/eftw1fys.aspx 문서를 참고하셔서
ICSharpCode.SharpZipLib 어셈블리가 0.83.1.0 버전대신 최신 버전을 바인딩하도록 설정해 주시면
될 겁니다.

#re: HTTP 압축 모듈 구현 !!! 함 써 보셈~ 2007-02-26 오후 3:48 임대열
네. 감사합니다. ^^;

말씀하신대로 configuration 을 설정하니 잘됩니다.

#re: HTTP 압축 모듈 구현 !!! 함 써 보셈~ 2007-03-02 오후 1:45 임대열
안녕하세요. 이걸 서버에 적용해서 테스트 하는 중입니다.
약 60MB bytes 의 XML Websercice 결과 데이터가 4.5MB bytes 로 줄어듭니다.
확실히 성능향상이 됨을 느낄수 있었습니다.
많은 도움되었습니다. 감사합니다. ^^

그런데.. 가끔씩 다음과 같은 에러가 발생합니다.

'' 16진수 값 0x1F은(는) 잘못된 문자입니다. 줄1, 위치1

위 에러는 주인장님 포스트에 있는 글에 의하면 압축을 해제 못할때에 나는 에러와 같습니다.
Fiddler 의 헤더값을 보니 Response Headers 의 Entity 에서 Content-Encoding:gzip 가 두번이 나옵니다.

아마 그래서 클라이언트에서 압축해제를 하지 못하나 하는 생각이 듭니다.

해당 호출 XML Webservice 함수 선언을 보니 그 메소드에는 Attribute 로 CacheDuration 이 설정되어 있습니다.
아마 이것때문이 아닌가 생각이 됩니다.
#re: HTTP 압축 모듈 구현 !!! 함 써 보셈~ 2007-03-02 오후 1:49 임대열
해당 웹서비스의 응용 프로그램 풀을 재시작하니 정상적으로 작동하네요.
아마 압축을 적용하기전에 캐시된 것이 압축적용후에 에러가 생기는 거 같습니다.

#re: HTTP 압축 모듈 구현 !!! 함 써 보셈~ 2007-03-02 오후 1:59 임대열
^^;

처음에 재시작할때는 정상적인데 다음번에 다시 호출하면 같은 에러가 발생합니다.

CacheDuration 으로 되어 있으면 작동을 안하나요?

#re: HTTP 압축 모듈 구현 !!! 함 써 보셈~ 2007-03-07 오후 6:47 임대열
이게 버그인지 아닌지 모르겠지만..

만약에 XML 웹서비스에서 메소드 호출동안에 Exception() 을 발생시킬 경우에
클라이언트에서 그 메소드 호출시 SoapException() 을 catch 하지 못해 버립니다.
Fiddler 로 TextView 부분을 보니까 앞부분에 gth: 663 이라는 이상한 값이 들어가 있네요.
아래는 SOAP 메시지 내용입니다.

----------------------------------------------------------------------------------------------------------------------------
gth: 663

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<soap:Fault>
<faultcode>soap:Server</faultcode>
<faultstring>System.Web.Services.Protocols.SoapException: 서버에서 요청을 처리할 수 없습니다. ---&gt; System.Exception: AlreadyUsingAccountException
at Webservice.Service1.LogOn(String userid, String password, String ip)
--- 내부 예외 스택 추적의 끝 ---</faultstring>
<detail />
</soap:Fault>
</soap:Body>
</soap:Envelope>
-------------------------------------------------------------------------------------------------------------------------------------------------------

웹메소드는 다음과 같은 exception 을 발생시킵니다.

[WebMethod]
public bool method1(...)
{
throw new System.Exception("AlreadyUsingAccountException");
}
#re: HTTP 압축 모듈 구현 !!! 함 써 보셈~ 2007-03-07 오후 6:54 임대열
너무 여기에 제 글만 많이 도배를 하는거 같아서 죄송합니다. ^^;

제가 바로 위에 쓴 에러가 이상하게 Fiddler 로 캡쳐를 할 경우에 발생합니다.
Fiddler 을 닫고 나서 프로그램 실행시키면 아무 이상 없네요. 아무래도 Fiddler 의 문제같습니다.

#re: HTTP 압축 모듈 구현 !!! 함 써 보셈~ 2007-03-07 오후 9:22 블로그쥔장
네.. 맞습니다...
정확한 상황은 저도 아직 파악 못했지만, ASP.NET 웹 서비스 호출을 Fiddler로 캡처하면
웹 서비스 클라이언트 측에서 결과를 받지 못하는 상황이 발생합니다.
웹 서비스의 결과에 Fiddler가 추가적인 바이트를 삽입하는 것 같아 보입니다만...
Fiddler 만한 다른 캡처 프로그램 찾기도 쉽지 않은 상황에서
Fiddler를 안 쓸 수도 없고...
저는 이런 상황에서는 Microsoft Network Monitor를 사용합니다만...
Network Monitor는 로컬 컴퓨터로 테스트하는 경우에 메시지를 캡처하지 못하며
Fiddler 만큼 HTTP 메시지의 내용을 다양하게 보여주지 못한다는 아픔도 있습니다.
#re: HTTP 압축 모듈 구현 !!! 함 써 보셈~ 2007-03-13 오후 6:41 임대열
Fiddler 가 새로운 버전이 있더군요.

업그레이드 하니 위와 같은 상황이 발생하지 않습니다. ^^
Posted by 퓨전마법사
,