HttpClient 요청 결과를 캐시하기

개인적으로 캐시를 서비스 레이어에서 할 지 HttpClient에서 할 지 고민됩니다.

2 Likes

클라이언트 측에서 캐시를 한다는 생각 자체를 해 본 적이 없었는데, 많이 흥미로운 주제네요.

저자는 Http Get 요청에 대해 캐시가 유용하다고 말하고 있는데,

One thing that is missing from the out-of-the box behaviours is caching of GET requests (only ones that can be cached!), …

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (_cache.TryGetValue(request.RequestUri!, out var response) && response is HttpResponseMessage res)
        {
            _logger.LogInformation($"Getting response from cache for {request.RequestUri}"); 
        } 
        else
        {
            res = await base.SendAsync(request, cancellationToken);
            res.EnsureSuccessStatusCode();
            res.Content = new NoDisposeStreamContent(res.Content);

            _cache.Set(request.RequestUri!, res, DateTimeOffset.Now.Add(_duration));
            _logger.LogInformation($"Adding response for {request.RequestUri} to cache for {_duration}");
        }

        return res; 
    }

아무리 Get 이라도, 이 캐시를 쓸 때는 좀 신중해야 할 필요가 있을 것 같은데, 주로 어떤 항목에 사용하시는지 여쭤봐도 될까요?

MSA로 k8s를 사용하다 보니 서비스간 원격 호출을 사용하는 경우가 종종 있습니다.

pod에서 response를 캐싱하지만 추가로 원격 호출을 하지 않고 캐싱된 데이터를 사용하는 것도 생각해 보는 중입니다.

1 Like

결국은 클라이언트가 캐시를 한다는 기본 구조는 동일한 것 같은데, 제가 염려하는 바는 클라이언트 캐시의 유효성입니다.

원글의 저자는 캐시의 기본 수명을 한 시간으로 설정하고 있는데,

public class CachingHandler : DelegatingHandler
{
    private static readonly TimeSpan _defaultDuration = TimeSpan.FromHours(1);

이 값은 보통 서버 측(파드 측) 캐싱 예제에서 많이 보이더군요.

그런데, 서버는 실제 데이터를 관리하므로, 기본 값이 큰 의미가 없습니다.
Get/Post/Put/Delete 을 처리할 때 마다 캐시를 업데이트 할 수 있으니까요.

그런데, 클라이언트 측은 그렇지가 않죠.

쿠버네티스까지 동원될 정도로 거대 사이트면 데이터의 업데이트가 매우 잦을 것이고, 클라이언트 측 캐시는 쉽게 무효화될 것이라는 것이 제 추측입니다.
(이 것이 분산 캐시를 채택하는 이유라고 생각합니다)

클라이언트 캐시의 유효성을 담보하는 다른 수단이라도 있으신지요?

염려되는 상황은 원격 호출로 인한 오류 발생이 더 큽니다.

pod 끼리 상호 통신을 하면서 서버이자 클라이언트가 되는데요.

원격 호출을 줄여서 얻는 이득이 더 큰 부분에 캐싱을 어떻게 할 까 고민하던 중이었어요.

갱신이 불필요한 기준정보 같은 건 한번 입력하면 바뀌는 일이 없지만 GET은 매우 빈번해서

1 Like

요 부분은 제가 고려하지 못한 부분이네요.

3 Likes

만약 캐시한다면 캐시 헤더를 기반으로 Response 캐시할 수도 있고, 혹은 라이브러리를 통해서 캐시하도록 하는 방식도 있으니 고민할 거리가 많을 것 같습니다. :slight_smile:

Cache-Control 과 캐시 헤더를 통한 HttpClient 캐시 글

https://mac-blog.org.ua/dotnet-http-client-cache/

WebSocket 기반 RPC와 인메모리 캐시로 통신 레이어 따로 구현

stale-while-revalidate 기반으로 간단하게 구현한 라이브러리

5 Likes