이 문서에서는 ASP.NET Core 앱에서 라이브러리를 HybridCache
구성하고 사용하는 방법을 설명합니다. 라이브러리에 대한 소개를 원하시면 HybridCache
캐싱 개요 섹션을 참조하세요.
라이브러리 가져오기
Microsoft.Extensions.Caching.Hybrid
패키지를 설치합니다.
dotnet add package Microsoft.Extensions.Caching.Hybrid
서비스 등록
서비스를 HybridCache
호출하여 의존성 주입 (DI) 컨테이너에 추가합니다.
// Add services to the container.
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddAuthorization();
builder.Services.AddHybridCache();
위의 코드는 기본 옵션으로 서비스를 등록합니다 HybridCache
. 등록 API는 옵션과 직렬화를 구성할 수도 있습니다.
캐시 항목 가져오기 및 저장
이 HybridCache
서비스는 키를 받아들이고 GetOrCreateAsync 두 개의 오버로드가 있는 메서드를 제공합니다.
- 팩터리 메서드입니다.
- 상태와 팩토리 메서드
메서드는 키를 사용하여 기본 캐시에서 개체를 검색하려고 합니다. 기본 캐시(캐시 누락)에서 항목을 찾을 수 없는 경우 보조 캐시가 구성되어 있는지 확인합니다. 데이터가 없는 경우(다른 캐시 누락) 팩터리 메서드를 호출하여 데이터 원본에서 개체를 가져옵니다. 그런 다음 기본 및 보조 캐시에 개체를 저장합니다. 개체가 주 또는 보조 캐시(캐시 적중)에 있으면 팩터리 메서드가 호출되지 않습니다.
이 HybridCache
서비스는 지정된 키에 대한 동시 호출자 하나만 팩터리 메서드를 호출하고 다른 모든 호출자는 해당 호출 결과를 기다립니다.
CancellationToken
에 전달된 값은 GetOrCreateAsync
에서 모든 동시 호출자들의 결합된 취소를 나타냅니다.
주 GetOrCreateAsync
오버로드
대부분의 시나리오에서는 무상태 오버로드 GetOrCreateAsync
를 사용하는 것이 좋습니다. 호출하는 코드는 비교적 간단합니다. 예를 들어 다음과 같습니다.
public class SomeService(HybridCache cache)
{
private HybridCache _cache = cache;
public async Task<string> GetSomeInfoAsync(string name, int id, CancellationToken token = default)
{
return await _cache.GetOrCreateAsync(
$"{name}-{id}", // Unique key to the cache entry
async cancel => await GetDataFromTheSourceAsync(name, id, cancel),
cancellationToken: token
);
}
public async Task<string> GetDataFromTheSourceAsync(string name, int id, CancellationToken token)
{
string someInfo = $"someinfo-{name}-{id}";
return someInfo;
}
}
캐시 키 지침
key
는 GetOrCreateAsync
에 전달될 때 캐시되는 데이터를 고유하게 식별해야 합니다.
- 원본에서 해당 데이터를 검색하는 데 사용되는 식별자 값의 관점에서 볼 수 있습니다.
- 애플리케이션에 캐시된 다른 데이터 측면에서.
두 형식의 고유성은 일반적으로 문자열 연결을 사용하여 서로 다른 부분으로 구성된 단일 키 문자열을 하나의 문자열로 연결하여 보장합니다. 예시:
cache.GetOrCreateAsync($"/orders/{region}/{orderId}", ...);
또는
cache.GetOrCreateAsync($"user_prefs_{userId}", ...);
키 체계가 유효하고 데이터가 혼동되지 않도록 하는 것은 호출자의 책임입니다.
캐시 키에서 외부 사용자 입력을 직접 사용하지 마세요. 예를 들어 사용자 인터페이스의 원시 문자열을 캐시 키로 사용하지 마세요. 이렇게 하면 임의 또는 의미 없는 키로 캐시를 범람하여 발생하는 무단 액세스 또는 서비스 거부 공격과 같은 보안 위험에 앱을 노출할 수 있습니다. 위의 유효한 예제에서 순서 및 사용자 기본 설정 데이터는 명확하게 구분되며 신뢰할 수 있는 식별자를 사용합니다.
-
orderid
및userId
내부적으로 생성된 식별자입니다. -
region
는 미리 정의된 알려진 영역 목록에서 열거 또는 문자열일 수 있습니다.
토큰(/
및 _
)에는 중요성을 두지 않습니다. 전체 키 값은 불투명 식별 문자열로 처리됩니다. 이 경우 캐시 함수 방식을 변경하지 않고 /
생략하고 _
생략할 수 있지만 일반적으로 구분 기호는 모호성을 방지하는 데 사용됩니다. 예를 들어 $"order{customerId}{orderId}"
사이에 혼동을 일으킬 수 있습니다.
-
customerId
42,orderId
123 -
customerId
421,orderId
23
앞의 두 예제 모두 캐시 키를 order42123
생성합니다.
이 지침은 string
, HybridCache
및 IDistributedCache
같은 IMemoryCache
기반 캐시 API에 동일하게 적용됩니다.
앞에서 언급된 유효한 키의 예시 중$"..."
인라인으로 보간된 문자열 구문은 GetOrCreateAsync
호출 바로 안에 있습니다. 이 구문은 많은 시나리오에서 키에 대한 HybridCache
할당할 필요가 없는 향후 개선이 가능하므로 string
사용할 때 권장됩니다.
추가 주요 고려 사항
- 키는 유효한 최대 길이로 제한할 수 있습니다. 예를 들어 기본
HybridCache
구현(AddHybridCache(...)
통해)은 기본적으로 키를 1024자로 제한합니다. 이 숫자는HybridCacheOptions.MaximumKeyLength
을 통해 구성할 수 있으며, 더 긴 키는 캐시 메커니즘을 우회하여 포화 상태를 방지합니다. - 키는 유효한 유니코드 시퀀스여야 합니다. 잘못된 유니코드 시퀀스가 전달되면 동작이 정의되지 않습니다.
- 프로세스
IDistributedCache
외 보조 캐시를 사용하는 경우 백 엔드 구현에 추가 제한이 적용될 수 있습니다. 가상의 예로 특정 백 엔드는 대/소문자를 구분하지 않는 키 논리를 사용할 수 있습니다. 기본값HybridCache
(viaAddHybridCache(...)
)은 혼동 공격 또는 별칭 공격을 방지하기 위해 이 시나리오를 검색합니다(비트 문자열 같음 사용). 그러나 이 시나리오에서는 충돌하는 키가 예상보다 빨리 덮어쓰이거나 제거될 수 있습니다.
대체 GetOrCreateAsync
오버로딩
대체 오버로드는 캡처된 변수 및 인스턴스별 콜백에서 약간의 오버헤드를 줄일 수 있지만, 더 복잡한 코드를 희생할 수 있습니다. 대부분의 시나리오에서 성능 향상은 코드 복잡성보다 크지 않습니다. 대체 오버로드를 사용하는 예제는 다음과 같습니다.
public class SomeService(HybridCache cache)
{
private HybridCache _cache = cache;
public async Task<string> GetSomeInfoAsync(string name, int id, CancellationToken token = default)
{
return await _cache.GetOrCreateAsync(
$"{name}-{id}", // Unique key to the cache entry
(name, id, obj: this),
static async (state, token) =>
await state.obj.GetDataFromTheSourceAsync(state.name, state.id, token),
cancellationToken: token
);
}
public async Task<string> GetDataFromTheSourceAsync(string name, int id, CancellationToken token)
{
string someInfo = $"someinfo-{name}-{id}";
return someInfo;
}
}
SetAsync
메서드
많은 시나리오 GetOrCreateAsync
에서 필요한 유일한 API입니다.
HybridCache
또한 SetAsync 개체를 먼저 검색하지 않고 캐시에 저장할 수 있는 기능이 있습니다.
키별 캐시 항목 제거
캐시 항목의 기본 데이터가 만료되기 전에 변경되는 경우 항목에 대한 키를 사용하여 호출 RemoveAsync 하여 항목을 명시적으로 제거합니다. 오버로드를 사용하면 키 값 컬렉션을 지정할 수 있습니다.
항목이 제거되면 주 캐시와 보조 캐시에서 모두 제거됩니다.
태그로 캐시 항목 제거
태그를 사용하여 캐시 항목을 그룹화하고 함께 무효화할 수 있습니다.
다음 예제와 같이 호출 GetOrCreateAsync
할 때 태그를 설정합니다.
public class SomeService(HybridCache cache)
{
private HybridCache _cache = cache;
public async Task<string> GetSomeInfoAsync(string name, int id, CancellationToken token = default)
{
var tags = new List<string> { "tag1", "tag2", "tag3" };
var entryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromMinutes(1),
LocalCacheExpiration = TimeSpan.FromMinutes(1)
};
return await _cache.GetOrCreateAsync(
$"{name}-{id}", // Unique key to the cache entry
async cancel => await GetDataFromTheSourceAsync(name, id, cancel),
entryOptions,
tags,
cancellationToken: token
);
}
public async Task<string> GetDataFromTheSourceAsync(string name, int id, CancellationToken token)
{
string someInfo = $"someinfo-{name}-{id}";
return someInfo;
}
}
지정된 태그에 대한 모든 항목을 제거하기 위해 태그 값을 사용하여 RemoveByTagAsync를 호출합니다. 오버로드를 사용하면 태그 값 컬렉션을 지정할 수 있습니다.
IMemoryCache
태그 개념에 대한 직접적인 지원도 IDistributedCache
없으므로 태그 기반 무효화는 논리적 작업일 뿐입니다. 로컬 또는 분산 캐시에서 값을 적극적으로 제거하지 않습니다. 대신 이러한 태그가 있는 데이터를 수신할 때 데이터가 로컬 및 원격 캐시 모두에서 캐시 누락으로 처리되도록 합니다. 값은 구성된 수명에 따라 일반적인 방식으로 만료 IMemoryCache
IDistributedCache
됩니다.
모든 캐시 항목 제거
별표 태그(*
)는 와일드카드로 예약되며 개별 값에 대해 허용되지 않습니다. 호출 RemoveByTagAsync("*")
은 태그가 없는 데이터조차도 모든HybridCache
데이터를 무효화하는 효과가 있습니다. 개별 태그와 마찬가지로 이는 논리적 작업이며 개별 값은 자연스럽게 만료될 때까지 계속 존재합니다. 글롭 스타일 일치는 지원되지 않습니다. 예를 들어 RemoveByTagAsync("foo*")
로 시작하는 모든 것을 제거하기 위해 foo
을(를) 사용할 수 없습니다.
추가 태그 고려 사항
- 시스템은 사용할 수 있는 태그 수를 제한하지 않지만 큰 태그 집합은 성능에 부정적인 영향을 미칠 수 있습니다.
- 태그는 비어 있거나 공백만 있거나 예약된 값
*
일 수 없습니다.
옵션
이 메서드를 AddHybridCache
사용하여 전역 기본값을 구성할 수 있습니다. 다음 예제에서는 사용 가능한 옵션 중 일부를 구성하는 방법을 보여줍니다.
// Add services to the container.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthorization();
builder.Services.AddHybridCache(options =>
{
options.MaximumPayloadBytes = 1024 * 1024;
options.MaximumKeyLength = 1024;
options.DefaultEntryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromMinutes(5),
LocalCacheExpiration = TimeSpan.FromMinutes(5)
};
});
이 GetOrCreateAsync
메서드는 특정 캐시 항목에 대한 전역 기본값을 재정의하기 위해 HybridCacheEntryOptions
개체를 사용할 수도 있습니다. 예를 들어 다음과 같습니다.
public class SomeService(HybridCache cache)
{
private HybridCache _cache = cache;
public async Task<string> GetSomeInfoAsync(string name, int id, CancellationToken token = default)
{
var tags = new List<string> { "tag1", "tag2", "tag3" };
var entryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromMinutes(1),
LocalCacheExpiration = TimeSpan.FromMinutes(1)
};
return await _cache.GetOrCreateAsync(
$"{name}-{id}", // Unique key to the cache entry
async cancel => await GetDataFromTheSourceAsync(name, id, cancel),
entryOptions,
tags,
cancellationToken: token
);
}
public async Task<string> GetDataFromTheSourceAsync(string name, int id, CancellationToken token)
{
string someInfo = $"someinfo-{name}-{id}";
return someInfo;
}
}
옵션에 대한 자세한 내용은 소스 코드를 참조하세요.
- HybridCacheOptions 클래스입니다.
- HybridCacheEntryOptions 클래스입니다.
제한
다음 속성을 HybridCacheOptions
사용하면 모든 캐시 항목에 적용되는 제한을 구성할 수 있습니다.
- MaximumPayloadBytes - 캐시 항목의 최대 크기입니다. 기본값은 1MB입니다. 이 크기에 대한 값 저장 시도가 기록되고 값이 캐시에 저장되지 않습니다.
- MaximumKeyLength - 캐시 키의 최대 길이입니다. 기본값은 1024자입니다. 이 크기에 대한 값 저장 시도가 기록되고 값이 캐시에 저장되지 않습니다.
직렬화
보조 프로세스 외 캐시를 사용하려면 직렬화가 필요합니다. 직렬화는 서비스 등록의 일부로 구성됩니다 HybridCache
. 형식별 및 범용 직렬 변환기는 AddSerializer 호출에서 AddSerializerFactory 및 AddHybridCache
메서드를 연결하여 구성할 수 있습니다. 기본적으로 라이브러리는 string
와 byte[]
를 내부적으로 처리하며, System.Text.Json
를 다른 모든 항목에 사용합니다.
HybridCache
는 protobuf 또는 XML과 같은 다른 직렬 변환기를 사용할 수도 있습니다.
다음 예제에서는 형식별 protobuf 직렬 변환기를 사용하도록 서비스를 구성합니다.
// Add services to the container.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthorization();
builder.Services.AddHybridCache(options =>
{
options.DefaultEntryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromSeconds(10),
LocalCacheExpiration = TimeSpan.FromSeconds(5)
};
}).AddSerializer<SomeProtobufMessage,
GoogleProtobufSerializer<SomeProtobufMessage>>();
다음 예제에서는 많은 protobuf 형식을 처리할 수 있는 범용 protobuf 직렬 변환기를 사용하도록 서비스를 구성합니다.
// Add services to the container.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthorization();
builder.Services.AddHybridCache(options =>
{
options.DefaultEntryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromSeconds(10),
LocalCacheExpiration = TimeSpan.FromSeconds(5)
};
}).AddSerializerFactory<GoogleProtobufSerializerFactory>();
보조 캐시에는 Redis 또는 SqlServer와 같은 데이터 저장소가 필요합니다. Azure Cache for Redis를 사용하려면 다음을 수행합니다.
Microsoft.Extensions.Caching.StackExchangeRedis
패키지를 설치합니다.Azure Cache for Redis의 인스턴스를 만듭니다.
Redis 인스턴스에 연결하는 연결 문자열 가져옵니다. Azure Portal의 개요 페이지에서 액세스 키 표시를 선택하여 연결 문자열을 찾으십시오.
앱의 구성에 연결 문자열 저장합니다. 예를 들어, 다음과 같은 JSON 형식의 사용자 비밀 파일을 사용하고, 접속 문자열은
ConnectionStrings
섹션에 포함하세요.<the connection string>
를 실제 연결 문자열로 바꾸세요.{ "ConnectionStrings": { "RedisConnectionString": "<the connection string>" } }
Redis 패키지가
IDistributedCache
제공하는 구현을 DI에 등록합니다. 이 작업을 수행하려면AddStackExchangeRedisCache
을(를) 호출하고, 연결 문자열을 전달한다. 예시:builder.Services.AddStackExchangeRedisCache(options => { options.Configuration = builder.Configuration.GetConnectionString("RedisConnectionString"); });
Redis
IDistributedCache
구현은 이제 앱의 DI 컨테이너에서 사용할 수 있습니다.HybridCache
는 보조 캐시로 사용되며, 그것에 대해 구성된 직렬 변환기를 사용합니다.
자세한 내용은 HybridCache serialization 샘플 앱을 참조 하세요.
캐시 스토리지
기본적으로 HybridCache
기본 캐시 스토리지에 사용됩니다 MemoryCache . 캐시 항목은 프로세스에 저장되므로 각 서버에는 서버 프로세스가 다시 시작될 때마다 손실되는 별도의 캐시가 있습니다. Redis 또는 SQL Server와 같은 보조 프로세스 외부 스토리지의 경우, 구성된 HybridCache
구현IDistributedCache
을 사용합니다. 그러나
메모
키 또는 태그별로 캐시 항목을 무효화하는 경우 현재 서버와 보조 Out-of-process 스토리지에서 무효화됩니다. 그러나 다른 서버의 메모리 내 캐시는 영향을 받지 않습니다.
성능 최적화
성능을 최적화하려면 HybridCache
하고 byte[]
하도록 구성합니다.
개체 다시 사용
인스턴스 HybridCache
를 다시 사용하면 호출별 역직렬화와 관련된 CPU 및 개체 할당의 오버헤드를 줄일 수 있습니다. 이렇게 하면 캐시된 개체가 크거나 자주 액세스하는 시나리오에서 성능이 향상될 수 있습니다.
사용하는 IDistributedCache
일반적인 기존 코드에서는 캐시에서 개체를 검색할 때마다 역직렬화가 발생합니다. 이 동작은 각 동시 호출자가 다른 인스턴스와 상호 작용할 수 없는 개체의 별도 인스턴스를 가져옵니다. 동일한 개체 인스턴스를 동시에 수정할 위험이 없으므로 스레드 안전성도 발생합니다.
HybridCache
많은 사용량이 기존 IDistributedCache
코드 HybridCache
에서 조정되므로 동시성 버그가 발생하지 않도록 기본적으로 이 동작을 유지합니다. 그러나 다음과 같은 경우 개체는 기본적으로 스레드로부터 안전합니다.
- 변경할 수 없는 형식입니다.
- 코드는 그것들을 수정하지 않습니다.
이러한 경우 다음 중 하나 이상을 변경하여 인스턴스를 다시 사용하는 것이 안전하다는 것을 알릴 HybridCache
수 있습니다.
-
sealed
로 타입을 표시합니다. C#의 키워드는sealed
클래스를 상속할 수 없다는 것을 의미합니다. - 형식에
[ImmutableObject(true)]
특성 적용 이 특성은[ImmutableObject(true)]
개체를 만든 후에는 개체의 상태를 변경할 수 없다는 것을 나타냅니다.
byte[]
할당 피하기
HybridCache
은/는 구현에 대한 선택적 API를 제공하여, IDistributedCache
할당을 방지할 수 있습니다. 이 기능은 Microsoft.Extensions.Caching.StackExchangeRedis
및 Microsoft.Extensions.Caching.SqlServer
패키지의 미리 보기 버전에 의해 구현됩니다. 자세한 내용은 IBufferDistributedCache를 참조하세요.
패키지를 설치하는 .NET CLI 명령은 다음과 같습니다.
dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis
dotnet add package Microsoft.Extensions.Caching.SqlServer
사용자 지정 HybridCache 구현
추상 클래스의 HybridCache
구체적인 구현은 공유 프레임워크에 포함되며 종속성 주입을 통해 제공됩니다. 그러나 개발자는 FusionCache같은 API의 사용자 지정 구현을 제공하거나 사용할 수 있습니다.
네이티브 AOT에서 하이브리드 캐시 사용
다음 네이티브 AOT 관련 고려 사항이 HybridCache
에 적용됩니다.
직렬화
네이티브 AOT는 런타임 리플렉션 기반 serialization을 지원하지 않습니다. 사용자 지정 형식을 캐시하는 경우 원본 생성기를 사용하거나 원본 생성과 같은
System.Text.Json
AOT와 호환되는 serializer를 명시적으로 구성해야 합니다.HybridCache
아직 개발 중이며 AOT와 함께 사용하는 방법을 간소화하는 것이 해당 개발의 높은 우선 순위입니다. 자세한 내용은 끌어오기 요청 dotnet/extensions#6475를 참조하세요.손질
캐시하는 모든 형식이 AOT 컴파일러에서 트리밍되지 않도록 하는 방식으로 참조되는지 확인합니다. serialization에 원본 생성기를 사용하면 이 요구 사항에 도움이 됩니다. 자세한 내용은 네이티브 AOT에 대한 ASP.NET Core 지원을 참조하세요.
직렬화 및 트리밍을 올바르게 HybridCache
설정하는 경우 일반 ASP.NET Core 앱과 동일한 방식으로 네이티브 AOT에서 동작합니다.
호환성
라이브러리는 HybridCache
.NET Framework 4.7.2 및 .NET Standard 2.0까지 이전 .NET 런타임을 지원합니다.
추가 리소스
자세한 내용은 소스 코드를 참조 HybridCache
하세요.
ASP.NET Core