저자: Rob Howard 번역: 열대어
이 문서에서는 다음 내용을 설명합니다.
· 일반적인 ASP.NET 성능 비밀
· ASP.NET 성능을 향상시키는 유용한 팁과 요령
· ASP.NET에서 데이터베이스 사용에 대한 권장 사항
· ASP.NET의 캐싱 및 백그라운드 처리
ASP.NET을 사용하여 웹 애플리케이션을 작성하는 것은 매우 간단합니다. 너무 간단해서 많은 개발자가 제대로 작동하는 애플리케이션을 구축하는 데 시간을 들이지 않습니다. 이 기사에서는 고성능 웹 애플리케이션 작성을 위한 10가지 팁을 추천합니다. ASP.NET 응용 프로그램은 웹 응용 프로그램의 하위 집합일 뿐이므로 논의를 ASP.NET 응용 프로그램으로 제한하지 않겠습니다. 이 기사는 웹 애플리케이션의 성능을 최적화하기 위한 최종 가이드가 될 의도가 없습니다. 전체 책을 읽으면 쉽게 그렇게 할 수 있습니다. 대신, 우리는 이 글을 좋은 출발점으로 생각해야 합니다.
워커홀릭이 되기 전에는 암벽등반을 많이 하곤 했어요. 나는 등반을 하기 전에 여행 가이드에서 경로를 살펴보고 정상에 다녀온 사람들의 추천을 읽는 것을 선호합니다. 그러나 아무리 잘 작성된 가이드북이라 할지라도 도전적인 목표를 시도하기 전에는 실제 등반 경험이 필요합니다. 마찬가지로 성능 문제를 해결하거나 처리량이 높은 사이트를 실행하는 경우에만 고성능 웹 응용 프로그램을 작성하는 방법을 배울 수 있습니다.
저의 개인적인 경험은 Microsoft의 ASP.NET 팀에서 기초 프로그램 관리자로 일하고, www.asp.net을 유지 및 관리하고, 잘 알려진 여러 ASP.NET 응용 프로그램 중 하나인 Community Server를 설계하는 데 도움을 준 것입니다(ASP.NET 포럼 , .Text 및 nGallery가 하나의 플랫폼에 연결됨) 저에게 도움이 되었던 이러한 팁 중 일부가 귀하에게도 유용할 것이라고 확신합니다.
애플리케이션을 여러 논리적 계층으로 분리하는 것을 고려해야 합니다. 3계층(또는 n계층) 아키텍처에 대해 들어보셨을 것입니다. 이는 일반적으로 비즈니스 및/또는 하드웨어를 기능적 부문으로 물리적으로 나누는 규정된 구조 패턴입니다. 시스템에 더 큰 규모가 필요한 경우 더 많은 하드웨어를 쉽게 추가할 수 있습니다. 그러나 이는 비즈니스 및 시스템 점프와 관련된 성능 저하를 초래하므로 이를 피해야 합니다. 따라서 가능하면 ASP.NET 페이지와 페이지의 관련 구성 요소를 동일한 응용 프로그램에서 실행해 보십시오.
코드 분리 및 계층 간 경계로 인해 웹 서비스나 원격을 사용하면 성능이 20% 이상 저하될 수 있습니다.
데이터 계층은 약간 다릅니다. 일반적으로 데이터베이스 전용 하드웨어를 보유하는 것이 더 낫기 때문입니다. 그러나 데이터베이스로 프로세스를 점프하는 데 드는 비용은 여전히 높으므로 코드를 최적화할 때 데이터 계층의 성능을 가장 먼저 고려해야 합니다.
애플리케이션의 성능 문제를 해결하는 데 투자하기 전에 애플리케이션을 분석하여 문제의 근본 원인을 찾아내십시오. 핵심 성능 카운터(예: 가비지 수집을 수행하는 데 소요된 시간의 백분율을 나타내는 카운터)는 응용 프로그램이 대부분의 시간을 소비하는 위치를 찾는 데 매우 유용합니다. 시간을 보내는 장소는 덜 직관적인 경우가 많습니다.
이 기사에서는 성능을 향상시키는 두 가지 방법, 즉 ASP.NET 캐싱 사용과 같은 대규모 최적화와 자주 반복되는 소규모 최적화에 대해 설명합니다. 이러한 작은 최적화 부분이 때로는 가장 흥미롭습니다. 코드를 조금만 변경하면 수천 번 호출됩니다. 큰 청크를 최적화하면 전반적인 성능이 크게 향상될 수 있습니다. 작은 단위로 최적화하면 특정 요청에서 몇 마이크로초를 단축할 수 있지만 일일 모든 요청에 걸쳐 누적하면 예상치 못한 성능 향상을 얻을 수 있습니다.
데이터 계층의 성능
애플리케이션 성능 최적화를 시작할 때 우선순위를 정할 수 있는 결정적인 테스트가 하나 있습니다. 코드가 데이터베이스에 액세스해야 합니까? 그렇다면, 얼마나 자주 방문합니까? 이 테스트는 웹 서비스나 원격 제어를 사용하는 코드에도 적용될 수 있지만 이 기사에서는 이에 대해 다루지 않습니다.
코드의 특정 코드 경로에 데이터베이스 요청이 필요하고 문자열 조작과 같이 최적화의 우선순위를 지정하려는 다른 위치를 찾는 경우 중지하고 중요한 테스트를 먼저 수행하십시오. 처리해야 할 매우 나쁜 성능 문제가 없는 한, 데이터베이스에 연결하는 데 걸리는 시간, 반환되는 데이터의 양, 데이터베이스에서 수행하는 작업을 최적화하는 데 시간을 더 투자하는 것이 좋습니다.
이제 일반적인 정보를 다루었으므로 애플리케이션 성능을 향상시키는 데 도움이 되는 10가지 팁을 살펴보겠습니다. 성능 향상에 가장 확실한 영향을 미치는 것부터 시작하겠습니다.
팁 1 - 여러 결과 세트 반환
데이터베이스에 두 번 이상 액세스하는 요청 경로가 있는지 확인하려면 데이터베이스 코드를 살펴보세요. 이러한 각 왕복은 애플리케이션이 초당 처리할 수 있는 요청 수를 줄입니다. 단일 데이터베이스 요청으로 여러 결과 세트를 반환하면 데이터베이스 통신에 소요되는 전체 시간을 줄일 수 있습니다. 데이터베이스 서버가 관리해야 하는 요청 수를 줄이면 시스템 확장성이 향상됩니다.
일반적으로 동적 SQL 문을 사용하여 여러 결과 집합을 반환할 수 있습니다. 저는 저장 프로시저를 사용하는 것을 선호합니다. 저장 프로시저에 비즈니스 로직을 넣어야 하는지에 대해서는 논쟁의 여지가 있지만 저장 프로시저의 로직이 반환되는 데이터를 제한할 수 있다면(데이터 세트의 크기, 네트워크 연결에 소요되는 시간을 줄이고 논리 레이어 데이터 필터링), 그러면 좋은 것입니다.
SqlCommand 인스턴스와 해당 ExecuteReader 메서드를 사용하여 강력한 형식의 비즈니스 클래스를 생성하면 NextResult를 호출하여 결과 집합 포인터를 앞으로 이동할 수 있습니다. 그림 1은 정의된 클래스를 사용하여 여러 ArrayList를 생성하는 예제 세션을 보여줍니다. 데이터베이스에서 필요한 데이터만 반환하면 서버의 메모리 요청이 크게 줄어듭니다.
1// 첫 번째 결과 집합을 읽습니다.
2reader = 명령.ExecuteReader();
3
4// 해당 결과 집합에서 데이터를 읽습니다.
5while(reader.Read()) {
6개 공급자.Add(PopulateSupplierFromIDataReader(reader));
7}
8
9// 다음 결과 집합을 읽습니다.
10reader.NextResult();
11
12// 두 번째 결과 집합에서 데이터를 읽습니다.
13while(reader.Read()) {
14개 제품.Add(PopulateProductFromIDataReader(reader));
15}
16
17
팁 2 - 페이지를 매긴 데이터 액세스
ASP.NET의 DataGrid는 데이터 페이징 지원이라는 뛰어난 기능을 제공합니다. DataGrid에서 페이지 매김을 설정하면 한 번에 특정 수의 결과가 표시됩니다. 또한 결과 간 탐색을 위한 페이징 UI가 DataGrid 하단에 표시됩니다. 페이지가 매겨진 UI를 사용하면 표시된 데이터를 앞뒤로 탐색하여 페이지당 특정 수의 결과를 표시할 수 있습니다.
하지만 작은 문제가 있습니다. 페이징을 위해 DataGrid를 사용하는 경우 모든 데이터를 테이블에 바인딩해야 합니다. 예를 들어 데이터 레이어는 모든 데이터를 반환해야 하며, 그런 다음 DataGrid는 현재 페이지를 기반으로 표시할 모든 레코드를 채워야 합니다. DataGrid 페이지 매김을 사용할 때 100,000개의 레코드가 반환되면 요청당 99,975개의 레코드가 삭제됩니다(각 페이지의 용량이 25개 레코드라고 가정). 레코드 수가 증가하면 각 요청마다 더 많은 데이터가 반환되어야 하므로 애플리케이션 성능에 큰 영향을 미칩니다.
더 나은 페이지 매김 코드를 작성하는 한 가지 방법은 저장 프로시저를 사용하는 것입니다. 그림 2는 Nothwind 데이터베이스의 Orders 데이터 테이블을 페이징하는 샘플 저장 프로시저를 보여줍니다. 기본적으로 여기에서 해야 할 일은 페이지의 색인과 페이지의 용량을 전달하는 것뿐입니다. 데이터베이스는 적절한 결과 세트를 계산하고 이를 반환합니다.
1프로시저 만들기 northwind_OrdersPaged
2(
3 @PageIndex 정수,
4 @PageSize 정수
5)
6AS
7시작
8DECLARE @PageLowerBound 정수
9DECLARE @PageUpperBound 정수
10DECLARE @RowsToReturn 정수
11
12-- 먼저 행 개수를 설정합니다.
13SET @RowsToReturn = @PageSize * (@PageIndex + 1)
14SET ROWCOUNT @RowsToReturn
15
16--페이지 경계 설정
17SET @PageLowerBound = @PageSize * @PageIndex
18SET @PageUpperBound = @PageLowerBound + @PageSize + 1
19
20-- 선택 결과를 저장할 임시 테이블 생성
21CREATE TABLE #페이지인덱스
스물둘(
23 IndexId int IDENTITY (1, 1) NULL이 아님,
24 주문 ID 정수
25)
26
27--임시 테이블에 삽입
28#PageIndex(주문 ID)에 삽입
29선택
30 주문ID
31에서
32 주문
33주문
34 주문 ID DESC
35
36--반환 총 개수
37주문에서 COUNT(OrderID) 선택
38
39-- 페이징된 결과 반환
40SELECT
41오.*
42FROM
43 주문
44 #페이지인덱스 페이지인덱스
45어디서
46 O.OrderID = 페이지인덱스.주문ID AND
47 PageIndex.IndexID > @PageLowerBound AND
48 페이지인덱스.인덱스ID < @PageUpperBound
49주문
50 페이지인덱스.인덱스ID
51
52END
53
54
커뮤니티 서비스 기간 동안 이러한 데이터 페이징을 수행하기 위해 페이징 서버 컨트롤을 작성했습니다. 팁 1에서 설명한 아이디어를 사용하여 저장 프로시저에서 총 레코드 수와 요청된 데이터라는 두 가지 결과 집합을 반환했다는 것을 알 수 있습니다.
반환된 총 레코드 수는 수행된 요청에 따라 달라질 수 있습니다. 예를 들어 WHERE 절을 사용하여 반환되는 데이터를 제한할 수 있습니다. 페이지를 매긴 UI에 표시할 총 페이지 수를 계산하려면 반환할 총 레코드 수를 알아야 합니다. 예를 들어 총 레코드가 1,000,000개이고 WHERE 절을 사용하여 해당 레코드를 1,000개 레코드로 필터링하는 경우 페이징 UI를 적절하게 제출하려면 페이징 논리가 총 레코드 수를 알아야 합니다.
팁 3 - 연결 풀링
웹 애플리케이션과 SQL Server 간에 TCP 연결을 설정하는 것은 비용이 많이 드는 작업일 수 있습니다. Microsoft의 개발자는 한동안 연결 풀링을 활용하여 데이터베이스에 대한 연결을 재사용해 왔습니다. 각 요청마다 새로운 TCP 연결을 설정하는 대신 연결 풀에 사용 가능한 연결이 없는 경우에만 새 연결을 설정합니다. 연결이 닫히면 연결 풀로 반환됩니다. 즉, TCP 연결을 완전히 파괴하는 대신 데이터베이스에 대한 연결을 계속 유지합니다.
물론 연결 유출에 주의해야 합니다. 연결 사용이 끝나면 항상 연결을 닫으십시오. 반복합니다. Microsoft .NET Framework의 가비지 수집 메커니즘에 대해 누군가가 뭐라고 말하더라도 연결 작업이 끝나면 항상 연결에서 Close 또는 Dispose 메서드를 명시적으로 호출해야 합니다. 미리 결정된 시간에 연결을 정리하고 닫는 CLR(공용 언어 런타임)을 신뢰하지 마십시오. CLR은 결국 클래스를 삭제하고 연결을 강제로 닫지만 개체에 대한 가비지 수집 메커니즘이 실제로 수행되는 시기는 보장할 수 없습니다.
연결 풀링을 사용하여 최상의 결과를 얻으려면 몇 가지 규칙을 따라야 합니다. 먼저 연결을 열고 작업을 수행한 다음 연결을 닫습니다. 요청당 연결을 여러 번 열고 닫아야 하는 경우(가급적 팁 1 적용) 괜찮습니다. 연결을 열어두고 여러 다른 메서드에 전달하는 것보다 훨씬 낫습니다. 둘째, 동일한 연결 문자열을 사용합니다(통합 인증을 사용하는 경우 물론 동일한 스레드 ID). 동일한 연결 문자열(예: 로그인한 사용자를 기반으로 하는 다른 사용자 지정 연결 문자열)을 사용하지 않으면 연결 풀이 제공하는 것과 동일한 최적 값을 얻을 수 없습니다. 그리고 다수의 사용자를 사칭할 때 통합 인증을 사용하면 연결 풀의 효율성도 훨씬 떨어집니다. .NET CLR 데이터 성능 카운터는 연결 풀링과 관련된 성능 문제를 추적하려고 할 때 유용할 수 있습니다.
애플리케이션이 데이터베이스와 같은 리소스에 연결되거나 다른 프로세스에서 실행될 때마다 리소스에 연결하는 데 걸리는 시간, 데이터를 보내고 받는 데 걸리는 시간, 연결되는 시간에 중점을 두고 이를 수행해야 합니다. 데이터를 보내고 받는 데는 최적화를 위해 데이터베이스에 대한 왕복 횟수가 많이 있습니다. 애플리케이션에서 모든 종류의 프로세스 홉을 최적화하는 것은 더 나은 성능을 달성하기 위한 첫 번째 단계입니다.
애플리케이션 계층에는 데이터 계층에 연결하고 데이터를 의미 있는 클래스 인스턴스와 논리적 프로세스로 변환하는 논리가 포함되어 있습니다. 예를 들어 커뮤니티 서버에서는 포럼이나 스레드 컬렉션을 생성하고 권한과 같은 비즈니스 규칙을 적용하는 곳이며, 더 중요한 것은 캐싱 논리가 수행되는 곳입니다.
팁 4 - ASP.NET 버퍼링 API
응용 프로그램에서 첫 번째 코드 줄을 작성하기 전에 가장 먼저 고려해야 할 사항은 ASP.NET의 캐싱 기능을 최대화하고 활용하도록 응용 프로그램 계층을 설계하는 것입니다.
구성 요소가 ASP.NET 응용 프로그램 내에서 실행되는 경우 응용 프로그램 프로젝트에서 System.Web.dll을 참조하기만 하면 됩니다. 캐시에 액세스해야 하는 경우 HttpRuntime.Cache 속성을 사용하세요. 이 개체는 Page.Cache 및 HttpContext.Cache를 통해서도 액세스할 수 있습니다.
캐시된 데이터 사용에 대한 몇 가지 지침이 있습니다. 첫째, 데이터를 여러 번 사용할 수 있는 경우 해당 데이터를 캐싱하는 것이 좋은 선택입니다. 둘째, 데이터가 일반적이고 특정 요청이나 사용자에게 국한되지 않는 경우 해당 데이터를 캐싱하는 것이 좋은 옵션입니다. 데이터가 사용자 또는 요청별 데이터이지만 수명이 긴 경우 캐시할 수 있지만 자주 사용되지 않을 수 있습니다. 셋째, 종종 간과되는 원칙은 때로는 너무 많은 캐시를 캐시할 수 있다는 것입니다. 일반적으로 x86 시스템에서는 메모리 부족 오류 가능성을 줄이기 위해 800MB 이하의 전용 바이트를 사용하는 프로세스를 실행하는 것이 좋습니다. 따라서 캐싱은 제한되어야 합니다. 즉, 한 계산의 결과를 재사용해야 할 수도 있지만 해당 계산에 10개의 매개변수가 필요한 경우 10개의 순열을 캐시해야 할 수 있으며 이로 인해 문제가 발생할 수 있습니다. 오버캐싱으로 인한 메모리 부족 오류는 ASP.NET에서 가장 일반적이며, 특히 대용량 데이터 세트의 경우 더욱 그렇습니다.
캐싱에는 알아야 할 몇 가지 훌륭한 기능이 있습니다. 첫째, 캐시는 가장 최근에 사용된 알고리즘을 구현하여 ASP.NET이 메모리가 덜 효율적으로 실행될 때 강제로 캐시 플러시(캐시에서 사용하지 않는 항목을 자동으로 제거)를 수행할 수 있도록 합니다. 둘째, 캐시는 강제로 만료될 수 있는 만료된 종속성을 지원합니다. 이러한 종속성에는 시간, 키 및 파일이 포함됩니다. 시간이 자주 사용되지만 ASP.NET 2.0에서는 데이터베이스 캐시 무효화라는 새롭고 더욱 강력한 무효화 유형이 도입되었습니다. 데이터베이스의 데이터가 변경되면 캐시에 있는 항목을 자동으로 삭제하는 것을 말합니다. 데이터베이스 캐시 무효화에 대한 자세한 내용은 MSDN Magazine 2004년 7월호의 Dino Esposito Cutting Edge 칼럼을 참조하십시오. 캐시 아키텍처를 이해하려면 그림 3을 참조하세요.
팁 5 — 요청별 캐싱
이 기사 앞부분에서 자주 통과하는 코드 경로를 조금만 개선하면 전반적인 성능이 크게 향상될 수 있다고 언급했습니다. 이러한 작은 개선 사항 중 제가 가장 좋아하는 것은 요청별 캐싱이라고 부르는 것입니다.
캐싱 API는 장기간 또는 특정 조건이 충족될 때까지 데이터를 캐시하도록 설계되었지만 요청별 캐싱은 해당 요청 기간 동안에만 데이터를 캐싱하는 것을 의미합니다. 각 요청에 대해 특정 코드 경로에 자주 액세스하지만 데이터는 한 번만 추출, 적용, 수정 또는 업데이트됩니다. 이것은 다소 이론적인 것처럼 들리므로 구체적인 예를 들어보겠습니다.
커뮤니티 서버 포럼 애플리케이션에서 페이지에 사용되는 각 서버 컨트롤에는 사용할 모양, 사용할 스타일 테이블 및 기타 개인화 데이터를 결정하기 위한 개인화 데이터가 필요합니다. 이 데이터 중 일부는 장기간 캐시될 수 있지만 일부 데이터는 요청당 한 번만 가져온 다음 해당 요청 중에 컨트롤 표시 등을 위해 여러 번 재사용됩니다.
요청별 캐싱을 달성하려면 ASP.NET HttpContext를 사용하세요. 각 요청에 대해 HttpContext 인스턴스가 생성되고 요청 중에 HttpContext.Current 속성 내 어디에서나 액세스할 수 있습니다. HttpContext 클래스에는 특별한 Items 컬렉션 속성이 있으며 이 Items 컬렉션에 추가된 데이터는 요청 기간 동안에만 캐시됩니다. 캐싱을 사용하여 자주 액세스하는 데이터를 저장할 수 있는 것처럼 HttpContext.Items를 사용하여 요청별로만 사용되는 데이터를 저장할 수도 있습니다. 그 뒤에 있는 논리는 매우 간단합니다. 데이터가 존재하지 않을 때 HttpContext.Items 컬렉션에 데이터가 추가되고 후속 조회에서는 HttpContext.Items의 데이터만 반환됩니다.
팁 6 — 백그라운드 처리
코드로 가는 경로는 최대한 빨라야 합니다. 그렇죠? 매 요청 또는 매 n개 요청에 대해 수행되는 매우 리소스 집약적인 작업을 수행하고 있는 경우가 있을 수 있습니다. 이메일을 보내거나 들어오는 데이터를 분석하고 검증하는 것이 몇 가지 예입니다.
ASP.NET 포럼 1.0을 분석하고 커뮤니티 서버를 구성하는 콘텐츠를 다시 설계할 때 새 게시물을 게시하기 위한 코드 경로가 매우 느리다는 사실을 발견했습니다. 새 게시물이 게시될 때마다 애플리케이션은 먼저 중복된 게시물이 없는지 확인한 다음 "나쁜 단어" 필터를 사용하여 게시물을 분석하고 게시물의 문자 이모티콘을 분석하고 게시물에 태그를 지정하고 색인을 생성한 다음 요청 시 요청에 게시합니다. 적절한 대기열에 추가하고 첨부 파일을 확인한 다음 마지막으로 게시물이 게시된 후 즉시 모든 구독자에게 이메일 알림을 보냅니다. 분명히 많은 내용이 관련되어 있습니다.
조사 결과, 대부분의 시간이 로직을 인덱싱하고 이메일을 보내는 데 소요되는 것으로 나타났습니다. 게시물 인덱싱은 매우 시간이 많이 걸리는 작업이며 내장된 System.Web.Mail 기능이 SMTP 서버에 연결한 다음 지속적으로 이메일을 보내는 것으로 나타났습니다. 특정 게시물이나 주제 영역에 대한 구독자 수가 증가하면 AddPost 기능을 실행하는 데 시간이 점점 더 오래 걸립니다.
모든 요청에 이메일 인덱싱이 필요한 것은 아닙니다. 이상적으로는 이 작업을 일괄 처리하여 한 번에 25개의 게시물을 색인화하거나 5분마다 모든 이메일을 보내는 것을 원합니다. 우리는 Visual Studio 2005에 최종적으로 포함된 데이터 캐시 무효화의 프로토타입을 만드는 데 사용한 코드를 사용하기로 결정했습니다.
System.Threading 네임스페이스의 Timer 클래스는 매우 유용하지만 적어도 웹 개발자들 사이에서는 .NET Framework에서 잘 알려져 있지 않습니다. 일단 생성되면 이 Timer 클래스는 ThreadPool의 스레드에 대해 구성 가능한 간격으로 지정된 콜백을 호출합니다. 즉, ASP.NET 응용 프로그램에 대한 요청이 들어오지 않고도 실행되도록 코드를 설정할 수 있으며 이는 백그라운드 처리에 이상적입니다. 이 백그라운드 프로세스에서 색인화 또는 이메일 전송과 같은 작업을 수행할 수도 있습니다.
그러나 이 기술에는 몇 가지 문제점이 있습니다. 애플리케이션 도메인이 제거되면 이 타이머 인스턴스는 이벤트 발생을 중지합니다. 또한 CLR에는 프로세스당 스레드 수에 대한 엄격한 표준이 있기 때문에 부하가 심한 서버에서는 타이머가 스레드가 작업을 계속 완료하는 것을 보장하지 못하고 어느 정도 지연이 발생할 수 있는 상황이 있을 수 있습니다. . ASP.NET은 프로세스에서 특정 수의 스레드를 사용 가능하게 유지하고 요청 처리에 전체 스레드 중 일부만 사용하여 이러한 상황이 발생할 가능성을 최소화하려고 합니다. 그러나 비동기 작업이 많은 경우에는 문제가 될 수 있습니다.
여기에는 이 코드를 위한 공간이 충분하지 않지만 www.rob-howard.net 에서 이해하기 쉬운 예제를 다운로드할 수 있습니다. Blackbelt TechEd 2004 프레젠테이션의 슬라이드와 데모를 확인해 보세요.
팁 7 — 페이지 출력 캐싱 및 프록시 서버
ASP.NET은 프레젠테이션 계층입니다(또는 프레젠테이션 계층이어야 함). 이는 페이지, 사용자 컨트롤, 서버 컨트롤(HttpHandler 및 HttpModules) 및 이들이 생성하는 콘텐츠로 구성됩니다. 출력(HTML, XML, 이미지 또는 기타 데이터)을 생성하는 ASP.NET 페이지가 있고 모든 요청에 대해 이 코드를 실행하면 동일한 출력이 생성되는 경우 A에 사용할 수 있는 도구가 있습니다. 페이지 출력 캐싱에 대한 훌륭한 대안입니다.
페이지 상단에 다음 줄을 추가합니다.
<%@ 페이지 OutputCache VaryByParams="none" Duration="60" %>
이 페이지에 대한 출력을 한 번 생성한 다음 최대 60초 동안 여러 번 재사용할 수 있습니다. 이때 페이지가 다시 실행되고 출력이 ASP.NET 캐시에 다시 추가됩니다. 이 동작은 일부 저수준 프로그래밍 가능 API를 사용하여 수행할 수도 있습니다. 방금 언급한 VaryByParams 속성과 같이 출력 캐싱에 대해 구성 가능한 여러 설정이 있습니다. VaryByParams는 방금 요청되었지만 HTTP GET 또는 HTTP POST 매개변수를 지정하여 캐시 항목을 변경할 수도 있습니다. 예를 들어, default.aspx?Report=1 또는 default.aspx?Report=2에 대한 출력을 캐시하려면 VaryByParam="Report"를 설정하기만 하면 됩니다. 세미콜론으로 구분된 목록을 지정하여 추가 매개변수를 지정할 수 있습니다.
많은 사람들은 출력 캐싱을 사용할 때 ASP.NET 페이지가 Microsoft Internet Security and Acceleration Server 또는 Akamai에서 사용하는 것과 같은 캐싱 서버로 흐르는 HTTP 헤더도 생성한다는 사실을 인식하지 못합니다. HTTP 캐시 테이블 헤더를 설정하면 이러한 네트워크 리소스에 문서를 캐시할 수 있으며 원본 서버로 돌아가지 않고도 클라이언트 요청을 충족할 수 있습니다.
따라서 페이지 출력 캐싱을 사용한다고 해서 애플리케이션이 더 효율적이지는 않지만 다운스트림 캐싱 기술이 문서를 캐시하기 때문에 서버의 로드를 줄일 수 있습니다. 물론 이는 익명 콘텐츠일 수 있습니다. 일단 다운스트림으로 이동하면 요청을 다시 볼 수 없으며 더 이상 해당 콘텐츠에 대한 액세스를 방지하기 위한 인증을 수행할 수 없습니다.
팁 8 - IIS 6.0을 실행하세요(커널 캐시를 사용하려는 경우에도)
IIS 6.0(Windows Server 2003)을 실행하지 않는 경우 Microsoft 웹 서버의 뛰어난 성능 향상 기능을 놓치게 됩니다. 팁 7에서는 출력 캐싱에 대해 설명했습니다. IIS 5.0에서 요청은 IIS를 거쳐 ASP.NET으로 전달됩니다. 캐싱과 관련하여 ASP.NET의 HttpModule은 요청을 수신하고 캐시 내용을 반환합니다.
IIS 6.0을 사용하는 경우 ASP.NET에 대한 코드 변경이 필요하지 않은 커널 캐시라는 멋진 작은 기능을 찾을 수 있습니다. ASP.NET에서 출력 캐싱을 요청하면 IIS 커널 캐시는 캐시된 데이터의 복사본을 받습니다. 요청이 네트워크 드라이버에서 오면 커널 수준 드라이버(컨텍스트를 사용자 모드로 전환하지 않음)가 요청을 수신하고 캐시된 데이터를 캐시된 경우 응답으로 플러시한 다음 실행을 완료합니다. 즉, IIS 및 ASP.NET 출력 캐싱과 함께 커널 모드 캐싱을 사용하면 놀라운 성능 결과를 얻을 수 있습니다. Visual Studio 2005에서 ASP.NET을 개발하는 동안 저는 ASP.NET 성능을 담당하는 개발 관리자였습니다. 특정 작업은 개발자가 수행하지만 저는 매일 진행되는 모든 보고를 보게 됩니다. 커널 모드 캐시 결과는 항상 가장 흥미롭습니다. 가장 일반적인 특징은 네트워크가 요청/응답으로 넘쳐나는 반면 IIS는 약 5%의 CPU 사용량으로 실행된다는 것입니다. 정말 충격적이네요! 물론 IIS 6.0을 사용하는 다른 이유도 있지만 커널 모드 캐싱이 가장 확실한 이유입니다.
팁 9 - Gzip 압축 사용
gzip을 사용하는 것이 반드시 서버 성능을 높이는 방법은 아니지만(CPU 사용량이 증가할 수 있으므로) gzip 압축을 사용하면 서버에서 전송하는 바이트 수를 줄일 수 있습니다. 이로 인해 페이지 속도가 증가하고 대역폭 사용량이 감소합니다. 전송되는 데이터, 압축 가능 정도 및 클라이언트 브라우저의 지원 여부(IIS는 Internet Explorer 6.0 및 Firefox와 같이 gzip 압축을 지원하는 클라이언트에만 gzip 압축 콘텐츠를 보냅니다)에 따라 서버에서 더 많은 서비스를 제공할 수 있습니다. 요청. 실제로 반환되는 데이터 양을 줄일 때마다 거의 초당 요청 수가 늘어납니다.
Gzip 압축은 IIS 6.0에 내장되어 있으며 IIS 5.0에서 사용되는 gzip 압축보다 성능이 훨씬 좋습니다. 이는 좋은 소식입니다. 불행하게도 IIS 6.0에서 gzip 압축을 켜려고 하면 IIS의 속성 대화 상자에서 해당 설정을 찾지 못할 수도 있습니다. IIS 팀은 뛰어난 gzip 기능을 서버에 구축했지만 이를 활성화하기 위한 관리 UI를 포함하는 것을 잊어버렸습니다. gzip 압축을 활성화하려면 (심심해지지 않도록) IIS 6.0의 XML 구성 설정을 자세히 살펴봐야 합니다. 덧붙여서 OrcsWeb에서 호스팅되는 www.asp.net 서버에서 이 문제를 제기하는 데 도움을 준 OrcsWeb의 Scott Forsyth에게 감사의 뜻을 전합니다.
이 문서에서는 단계를 설명하지 않습니다. IIS6 압축에서 Brad Wilson의 문서를 읽어보세요. IIS에서 ASPX 압축 활성화에는 ASPX 압축 활성화에 대한 기술 자료 문서도 있습니다. 그러나 일부 구현 세부 사항으로 인해 동적 압축과 커널 캐싱은 IIS 6.0에서 동시에 존재할 수 없습니다.
팁 10 - 서버 컨트롤 뷰 상태
뷰 상태는 생성된 페이지의 숨겨진 출력 필드에 일부 상태 데이터를 저장하는 ASP.NET의 흥미로운 이름입니다. 페이지가 서버로 다시 전송되면 서버는 이 뷰 상태 데이터를 구문 분석하고 유효성을 검사하고 페이지의 컨트롤 트리에 다시 적용할 수 있습니다. 상태 보기는 상태가 클라이언트에 지속되도록 하고 이 상태를 저장하는 데 쿠키나 서버 메모리가 필요하지 않기 때문에 매우 강력한 기능입니다. 많은 ASP.NET 서버 컨트롤은 뷰 상태를 사용하여 데이터 페이지를 매길 때 표시되는 현재 페이지를 저장하는 등 페이지 요소와 상호 작용하는 동안 생성된 설정을 유지합니다.
그러나 뷰 상태를 사용하는 데에는 몇 가지 단점도 있습니다. 첫째, 서비스가 제공되거나 요청될 때 페이지의 전체 로드가 증가합니다. 서버로 다시 전송된 뷰 상태 데이터를 직렬화하거나 역직렬화할 때도 추가 오버헤드가 발생합니다. 마지막으로 뷰 상태는 서버의 메모리 할당을 증가시킵니다.
여러 서버 컨트롤에는 필요하지 않은 경우에도 뷰 상태를 과도하게 사용하는 경향이 있으며 그 중 가장 주목할만한 것은 DataGrid입니다. ViewState 속성의 기본 동작은 켜져 있지만 필요하지 않은 경우 컨트롤이나 페이지 수준에서 끌 수 있습니다. 컨트롤 내에서 EnableViewState 속성을 false로 설정하거나 다음 설정을 사용하여 페이지에서 전체적으로 설정합니다.
<%@ 페이지 EnableViewState="false" %>
페이지를 다시 게시하지 않거나 요청할 때마다 항상 페이지의 컨트롤을 다시 생성하는 경우 페이지 수준에서 보기 상태를 비활성화해야 합니다.
요약
고성능 ASP.NET 응용 프로그램을 작성할 때 도움이 되는 몇 가지 팁을 제공했습니다. 이 기사의 앞부분에서 언급했듯이 이것은 ASP.NET 성능에 대한 최종 설명이 아니라 예비 가이드입니다. (ASP.NET 응용 프로그램 성능 향상에 대한 자세한 내용은 ASP.NET 성능 향상을 참조하십시오.) 특정 성능 문제를 해결하는 가장 좋은 방법은 사용자의 경험을 통해서만 찾을 수 있습니다. 하지만 이러한 팁은 여행에 대한 좋은 지침이 될 것입니다. 소프트웨어 개발에는 절대적인 요소가 거의 없습니다. 모든 애플리케이션은 고유합니다.
사이드바 "일반적인 성능 오해"를 참조하세요.
Rob Howard는 고성능 웹 애플리케이션, 지식 기반 관리 및 협업 시스템을 전문으로 하는 Telligent Systems의 창립자입니다. Rob은 이전에 Microsoft에 입사하여 ASP.NET 1.0, 1.1 및 2.0용 인프라 설계를 도왔습니다. Rob에게 연락하려면 [email protected]을 방문하십시오.
원본 링크: http://msdn.microsoft.com/msdnmag/issues/05/01/ASPNETPerformance/default.aspx