Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 2개 있습니다.)

MEF가 적용된 ASP.NET 웹 사이트를 제니퍼 닷넷으로 모니터링 해본 결과!

지인 한 분이, 이번 참에 MEF(Managed Extensibility Framework)를 공부할 겸 간단하게 prototype 형식의 웹 사이트를 하나 만들었다고 하시면서... MEF 적용 자체는 Biz 계층과 Dac 계층에 모두 적용을 했는데, 속도가 너무 안 나와서 Biz만 MEF를 적용하는 것이 나을지, 아니면 Dac 쪽만 MEF를 적용하는 것이 나을 지에 대해서 제게 의견을 구했습니다.

사실 ^^ 저도 MEF는 자료로 보기만 하고 직접 사용해 본적은 없기 때문에 ... 마침 잘 되었다 싶어서 그 분께 관련 테스트 소스를 요청해서 받았습니다. 그런 후 '제니퍼 닷넷'으로 검사해 보았는데요... 이번 글은 그에 대한 결과를 공유하는 차원에서 써 봅니다. ^^




최대한 재현을 잘 해내기 위해 소스 코드를 요청했지만, 비슷한 뼈대의 구성으로 ASP.NET에 MEF를 적용한 예제가 아래의 글에도 공개되어 있으니 이 글을 읽으시는 분들도 쉽게 재현을 할 수 있을 것입니다.

MEF with ASP.NET - "Hello World!"
; http://www.codeproject.com/KB/aspnet/MEFwithASPNET.aspx

소스 코드를 살펴보면 다음과 같은데요.

protected void Page_Load(object sender, EventArgs e) 
{
    DirectoryCatalog catalog = new DirectoryCatalog(
        System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"));
    CompositionContainer container = new CompositionContainer(catalog);
    container.ComposeParts(this);
}

지인이 보내주신 코드도 위의 방법과 크게 다르지 않았습니다.

실행하고 곧바로 default 웹 페이지를 방문했는 데, 과연 다음과 같이 3초 가까운 시간이 흐른 것을 확인할 수 있었습니다.

mef_perf_1.png

위의 화면에서 발견할 수 있는 또 다른 문제는 "CPU_T" 열에 있는 2,606ms 수치입니다. 즉, default.aspx 웹 페이지가 응답까지 걸린 시간이 2,789ms가 걸렸는데 그동안 계속해서 CPU가 거의 100% 일을 한 것이나 다름없다는 의미입니다. 보통, 데이터베이스 조회 시에 DB가 무거워서 GAP 시간이 수 초가 넘게 걸려도 웹 서버의 CPU는 50ms 이하로 일을 하는 경우가 대부분이어서 웹 서버 자체는 사실 부하가 심하지 않은 경우가 대부분입니다.

그런데, 위의 경우는 웹 페이지가 방문되는 횟수가 늘어날 수록 CPU는 더욱 심하게 일하게 되어 웹 서버 자체의 운영에 심각한 영향을 줄 수 있음을 보여주는 것입니다.

아쉽게도, 제니퍼 닷넷이 보여주는 '기본 프로파일 정보'에는 DB/WCF/COM+/ASMX/.NET Remoting 이외에는 어떤 부분에서 시간이 걸렸다는 것을 보여주지 않기 때문에 위의 X-View 상세 보기 화면에서는 더 이상의 단서를 발견할 수는 없었습니다.

그래도 ^^ 여기서 이야기가 끝이면 아마 이 글을 쓰지도 않았겠지요.

아직 많은 분들이 모르시지만 ^^ '제니퍼 닷넷'은 사용자 어셈블리의 모든 메서드 호출을 프로파일링 할 수 있는 기능이 추가되어 있습니다. 이 기능을 활성화 하려면 '제니퍼 닷넷 에이전트'가 설치된 폴더의 'profile.ini' 파일에서 제공되는 "application_allmethods" 옵션 값을 "1"로 바꿔주어야 합니다.

[methodprofile]
application_allmethods=1

이렇게 변경하고 웹 사이트를 재생(recycle)해주면 이제 사용자 어셈블리의 (생성자를 제외한) 모든 메서드 호출에 대해서 프로파일링을 남겨줍니다. 아래는 위의 MEF 웹 사이트를 방문했을 때 남겨진 프로파일링 기록입니다.

mef_perf_2.png

훨씬 자세해졌지요. ^^ 보시는 것처럼, 전체적인 ASP.NET 웹 페이지의 ProcessRequest 처리에는 3,655ms가 걸렸고 그 이하 Page_Load와 UserControl의 Page_Load에서 각각 1.5초씩 지연된 것을 볼 수 있습니다.

아하... 결국 MEFManager.Compose가 재귀 호출되면서 한 번 호출될 때마다 약 90ms씩 CPU를 사용하면서 지연이 된 것임을 위의 프로파일 결과로 한 눈에 알 수 있습니다.




정리해 보면, 웹 사이트의 'bin' 폴더에는 총 650KB 용량의 DLL 파일들이 놓여 있었고 MEF가 적용된 웹 페이지는 Page_Load 시마다 그 DLL을 모두 로드하고 .NET Reflection으로 Export 특성이 적용된 타입들을 찾아내는 작업을 하고 있는 것이었습니다.

위와 같은 상황을 전달해 드렸더니, 그 분은 MEF가 적용된 DLL을 별도의 폴더로 분리하고 DirectoryCatalog를 그 폴더로 지정해서 속도 향상을 보았다고 합니다.

그런데, 일단 제 의견은 "(DirectoryCatalog를 쓰는 한) MEF는 ASP.NET에 적용해서는 안된다"입니다. 왜냐하면, 프로젝트가 진행되면서 점점 더 MEF가 적용된 DLL은 늘어갈 것이고 그때 쯤이면 또다시 성능 문제가 불거져 나올 것이기 때문입니다.
(결국,,, 이 글을 통해서 제니퍼 닷넷의 application_allmethods 옵션에 대한 자연스러운 홍보가 된 것 같습니다. ^^)

추가: MEF의 DirectoryCatalog를 cache 해두면 위의 성능 문제를 개선할 수 있습니다.




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 7/10/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2011-12-21 11시52분
[짜두] DirectoryCatalog 를 사용할때 느려지는건 MEF 뿐만 아니라 CAG 에서도 마찬가지 였던거 같아요. DLL 갯수가 많을 수록 그리고 해당 DLL 에 화면이 많을수록 시간이 기하급수적으로 늘어났던거 같은데...
[guest]
2011-12-22 08시33분
그렇죠. ^^ CAG도 DirectoryCatalog 비슷한 형식으로 DLL을 읽어들이는 방식을 지원하고, 대개 그것을 쓰죠. 그런데... CAG를 ASP.NET에 사용하려는 노력은 없었기 때문에 Page_Load 시마다 발생하는 성능 저하 현상은 겪을 수 없었지만, MEF는 ASP.NET에 적용하려고 시도하고 있다는 점이 다릅니다. ^^ (사실... Global.asax 정도에서 처음 한번 느려지는 거라면 ASP.NET에서도 쓸만할 수 있겠지요.)
정성태
2011-12-22 08시44분
[짜두] 이것도 동적로딩 머 이런 방식으로 회피할 수 있는 방법이 먼가 있지 않을까요? 흠 ASP.NET 이라 그것도 좀 애매하긴 하네요. 어떤페이지가 언제 호출될 줄 알고... 저도 Kevin 님 의견에 전적 동의입니다~ㅎ
[guest]
2011-12-22 10시17분
[엄준일] 저도 이와 관련하여 여러 가지 테스트를 해 보니,
문제는 DI Container 가 문제더군요.

웹 사이트에 DI Container나 Injection 과 같은 프레임워크를 쓴다는 것은 성능을 일부 포기할 수 밖에 없더라구요.
CPU 꽤 잡아먹어요.
[guest]
2011-12-22 11시42분
"짜두" 님과 대화하다 보니... 왠지 DirectoryCatalog를 cache하라는 하늘의 메시지가 내려와서... 테스트해 보니 잘 되는군요. ^^ 다음의 글을 참고하세요.

MEF를 ASP.NET에 성능 손실 없이 적용하려면?
; http://www.sysnet.pe.kr/2/0/1204
정성태
2011-12-22 05시03분
[lancers] Page_Load마다 Catalog를 재구성한다는 건 엄청난 오버일 듯...
거기다 그 중에서도 가장 Cost가 쓴 DirectoryCatalog는 더 답이 안 나오겠죠.
그 분이 사용하셨다는 꽁수대로 한다면, 가장 성능을 높일 수 있는 방법은 폴더를 점점 더 잘게 찢어서 궁극적으로는 DLL당 폴더 하나씩 하는 수 밖에 없을 듯..
(그럴바엔 차라리 AssemblyCatalog를 쓰겠지만)

Cache를 하는 것이 당연히 방법인데, 이것마저도 런타임에 매번 최초 Discovery를 하는 것은 상당한 부담이 됩니다.
그래서 예전에 적용했던 방법이 DirectoryCatalog를 구성한 후 이 정보를 바탕으로 XML로 떨구는 툴을 하나 만들었습니다.
말하자면 DirectoryCatalog를 사전에 생성해두는 것과 같은 효과랄까..
그래서 이후 DirectoryCatalog가 아닌 Custom하게 만든 ConfigurationCatalog를 쓰는 형태로 하고..
실제 어셈블리 로딩은 특정 Type이 MEF에서 최초로 요청될 때 일어나는 것으로..

이론상 이렇게 해두고..
정말로 동적인 변경이 필요한 경우에는 새로운 어셈블리를 로딩한 후 Recomposition을 하면 됩니다. ㅋㅋㅋ
[guest]

... 46  47  [48]  49  50  51  52  53  54  55  56  57  58  59  60  ...
NoWriterDateCnt.TitleFile(s)
12741정성태7/29/202116298.NET Framework: 1082. Azure Active Directory - Microsoft Graph API 호출 방법파일 다운로드1
12740정성태7/29/202114823오류 유형: 747. SharePoint - InvalidOperationException 0x80131509
12739정성태7/28/202115207오류 유형: 746. Azure Active Directory - IDW10106: The 'ClientId' option must be provided.
12738정성태7/28/202116225오류 유형: 745. Azure Active Directory - Client credential flows must have a scope value with /.default suffixed to the resource identifier (application ID URI).
12737정성태7/28/202115275오류 유형: 744. Azure Active Directory - The resource principal named api://...[client_id]... was not found in the tenant
12736정성태7/28/202115582오류 유형: 743. Active Azure Directory에서 "API permissions"의 권한 설정이 "Not granted for ..."로 나오는 문제
12735정성태7/27/202115343.NET Framework: 1081. C# - Azure AD 인증을 지원하는 데스크톱 애플리케이션 예제(Windows Forms) [2]파일 다운로드1
12734정성태7/26/202132167스크립트: 20. 특정 단어로 시작하거나/끝나는 문자열을 포함/제외하는 정규 표현식 - Look-around
12733정성태7/23/202120169.NET Framework: 1081. Self-Contained/SingleFile 유형의 .NET Core/5+ 실행 파일을 임베딩한다면? [1]파일 다운로드2
12732정성태7/23/202113511오류 유형: 742. SharePoint - The super user account utilized by the cache is not configured.
12731정성태7/23/202115542개발 환경 구성: 584. Add Internal URLs 화면에서 "Save" 버튼이 비활성화 된 경우
12730정성태7/23/202117078개발 환경 구성: 583. Visual Studio Code - Go 코드에서 입력을 받는 경우
12729정성태7/22/202115435.NET Framework: 1080. xUnit 단위 테스트에 메서드/클래스 수준의 문맥 제공 - Fixture
12728정성태7/22/202115581.NET Framework: 1079. MSTestv2 단위 테스트에 메서드/클래스/어셈블리 수준의 문맥 제공
12727정성태7/21/202116668.NET Framework: 1078. C# 단위 테스트 - MSTestv2/NUnit의 Assert.Inconclusive 사용법(?) [1]
12726정성태7/21/202116443VS.NET IDE: 169. 비주얼 스튜디오 - 단위 테스트 선택 시 MSTestv2 외의 xUnit, NUnit 사용법 [1]
12725정성태7/21/202114954오류 유형: 741. Failed to find the "go" binary in either GOROOT() or PATH
12724정성태7/21/202117875개발 환경 구성: 582. 윈도우 환경에서 Visual Studio Code + Go (Zip) 개발 환경 [1]
12723정성태7/21/202114679오류 유형: 740. SharePoint - Alternate access mappings have not been configured 경고
12722정성태7/20/202114434오류 유형: 739. MSVCR110.dll이 없어 exe 실행이 안 되는 경우
12721정성태7/20/202115671오류 유형: 738. The trust relationship between this workstation and the primary domain failed. - 세 번째 이야기
12720정성태7/19/202114943Linux: 43. .NET Core/5+ 응용 프로그램의 Ubuntu (Debian) 패키지 준비
12719정성태7/19/202114129오류 유형: 737. SharePoint 설치 시 "0x800710D8 The object identifier does not represent a valid object." 오류 발생
12718정성태7/19/202114080개발 환경 구성: 581. Windows에서 WSL로 파일 복사 시 root 소유권으로 적용되는 문제파일 다운로드1
12717정성태7/18/202114339Windows: 195. robocopy에서 파일의 ADS(Alternate Data Stream) 정보 복사를 제외하는 방법
12716정성태7/17/202115379개발 환경 구성: 580. msbuild의 Exec Task에 robocopy를 사용하는 방법파일 다운로드1
... 46  47  [48]  49  50  51  52  53  54  55  56  57  58  59  60  ...