인메모리 캐시에 대한 나의 견해
저는 엄청난 양의 데이터를 처리하는 작업을 해왔습니다. 그렇게 하는 동안 나는 그것의 큰 덩어리가 결코 변하지 않거나 적어도 어느 정도 고정된 시간 동안은 변하지 않는다는 것을 깨달았습니다.
그래서 개인 캐시 저장소를 만드는 것이 유용할 것이라고 생각했습니다. 물론 이것은 새로운 것은 아닙니다. 몇 주 전에 Nick Craver가 작성한 StackOverflow의 포스트에서 애플리케이션 캐시를 관리하는 방법에 대해 읽었습니다.
또한 저는 항상 캐시를 사용하여 작업하고 싶었습니다. 캐시가 어떻게 작동하는지, 논리를 어떻게 작동하게 만들 수 있는지, 그래서… 안 될 이유가 없군요!
흐름
다음은 사용자가 값을 요청할 때 캐시를 제어하는 클래스가 어떻게 작동하는지에 대한 간단한 흐름입니다.
구현
시작하기 전에
알아요, 알아요. 메모리 캐시를 처리하는 System.Runtime.Caching가 있습니다. 하지만 제가 직접 만들기로 결정했습니다. 해당 클래스를 사용하려면 여기에서 방법을 확인하세요.
캐시항목
첫 번째 단계는 객체의 값과 만료 날짜를 저장하는 클래스를 만드는 것입니다. 아마도 더 좋은 방법이 있을 것입니다. 하지만 제 생각에는 다음과 같습니다.
public class CacheItem
{
public string Identifier { get; set; }
public object Value { get; set; }
public DateTime ValidUntil { get; set; }
public CacheItem(string identifier, object value, TimeSpan valid)
{
Identifier = identifier;
Value = value;
ValidUntil = DateTime.UtcNow.Add(valid);
}
}
캐시 저장소
그런 다음 객체를 처리하고 어딘가에 저장하는 클래스가 필요합니다(CacheItem). 나는 접미사 Repository가 있는 클래스의 모든 데이터/모델을 처리하고 싶지만 꼭 그럴 필요는 없으므로 캐싱을 위해 하나 만들어 보겠습니다.
public class CacheRepository
{
private static Dictionary<string, string> Cache = new Dictionary<string, string>();
private static T Set<T>(string key, Func<T> lookup, TimeSpan durationMinutes)
{
var item = new Models.Item(key, lookup(), durationMinutes);
return Save<T>(key, item);
}
private static T Save<T>(string key, Item item)
{
Cache[key] = JsonConvert.SerializeObject(item);
return (T)item.Value;
}
private static T Get<T>(string key)
{
var cached = Cache.FirstOrDefault(x => x.Key == key).Value;
var item = string.IsNullOrEmpty(cached) ? null : JsonConvert.DeserializeObject<Item>(cached);
return (item == null)
? default : (item.ValidUntil > DateTime.UtcNow)
? JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(item.Value)) : default;
}
public static T GetOrSet<T>(string key, Func<T> lookup, TimeSpan durationMinutes)
{
var cache = Get<T>(key);
return cache == null ? Set(key, lookup, durationMinutes) : cache;
}
}
지금까지 모든 항목이 저장될 Cache라는 정적 사전이 있습니다. 이는 애플리케이션이 실행되는 동안에만 지속되므로 이 자습서 제목에 메모리 내 캐싱이 있는 이유를 기억하세요.
Cache 항목은 CacheRepository 클래스가 로드되면 초기화된다는 점에 유의하세요.
CacheRepository 클래스를 호출할 때 사용할 수 있는 유일한 메서드는 세 가지 매개 변수가 필요한 GetOrSet(string key, Func<T> lookup, TimeSpan durationMinutes)입니다.
key: 저장할 객체의 식별자입니다.lookup: 캐시가 만료되었거나 null인 경우의 콜백 함수입니다.durationMinutes: 현재 시간(UTC)에 추가될 기간(분)입니다.
캐시할 시간
이제 캐싱 저장소를 사용하여 어딘가에서 일부 데이터를 가져옵니다.
이 모든 것이 이해되도록 하기 위해 몇 가지 속성이 포함된 예제 개체를 만든 다음 해당 개체의 목록을 가져와 채우는 저장소를 만들어 보겠습니다.
public class User
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
public class UserRepository
{
public List<User> Get()
{
// Code to get users
}
}
User 목록을 채울 수 있는 방법이 있으므로 CacheRepository 클래스를 활용해 보겠습니다.
private UserRepository _userRepository = new UserRepository();
private List<User> _user;
public List<User> User
{
get
{
_user = CacheRepository.GetOrSet($"users", usersRepo.Get, TimeSpan.FromMinutes(10));
return _user;
}
}
마찬가지로 User 변수에 액세스할 때마다 users 키가 있는 객체의 값을 CacheRepository에 요청합니다.
해당 키가 있으면 만료 날짜를 확인합니다. 이러한 조건 중 하나라도 거짓이면 콜백을 사용하여 객체의 값(usersRepo.Get 사용)을 설정하고 만료 날짜가 DateTime.UtcNow + TimeSpan.FromMinutes(10)로 설정된 캐시에 저장한 후 반환합니다.
