[HTTP] Cache

참고 도서 : HTTP 완벽 가이드

캐시

웹 캐시는 자주 쓰이는 문서의 사본을 자동으로 보관하는 HTTP 장치다. 웹 요청이 캐시에 도착했을 때, 캐시된 로컬 사본이 존재한다면 그 문서는 원 서버가 아니라 그 캐시로부터 제공된다.

  • 캐시는 불필요한 데이터 전송을 줄여 네트워크 요금 비용을 줄여준다
  • 캐시는 네트워크 병목을 줄여준다. 대역폭을 늘리지 않고 페이지를 빨리 불러올 수 있다
  • 캐시는 원 서버에 대한 요청을 줄여준다. 서버의 부하는 줄이고 더 빨리 응답할 수 있다
  • 페이지를 먼 곳에서 불러올수록 시간이 많이 걸리는데, 캐시는 거리로 인한 지연을 줄여준다

불필요한 데이터 전송

서버는 같은 클라이언트에게 정보를 전송하게 될때, 똑같은 바이트들이 네트워크를 통해 계속 반복해서 이동한다. 이 불필요한 데이터 전송은 값비싼 네트워크 대역폭을 잡아먹고, 전송을 느리게 만들고, 웹 서버에 부하를 준다.

캐시를 이용하면, 첫 번째 서버 응답은 캐시에 보관된다. 캐시된 사본이 뒤이은 요청들에 대한 응답으로 사용될 수 있기 때문에, 원 서버가 중복해서 트래픽을 주고받는 낭비가 줄어들게 된다.

대역폭 병목

캐시는 네트워크 병목을 줄여준다. 많은 네트워크가 원격 서버보다 로컬 네트워크 클라이언트에 더 넓은 대역폭을 제공한다. 많은 네트워크가 원격 서버보다 로컬 네트워크 클라이언트에 더 넓은 대역폭을 제공한다.

클라이언트가 서버에 접근할 때의 속도는 그 경로에 있는 가장 느린 네트워크의 속도와 같다. 클라이언트가 빠른 LAN에 있는 캐시로부터 사본을 가져온다면, 캐싱은 성능을 대폭 개선할 수 있다.

갑작스런 요청 쇄도 (Flash Crowds)

캐싱은 갑작스런 요청 쇄도에 대처하기 위해 특히 중요하다. 많은 사람이 거의 동시에 웹 문서에 접근할 때 이런 일이 발생한다. 이로 인한 불필요한 트래픽 급증은 네트워크와 웹 서버의 심각한 장애를 야기시킨다.

거리로 인한 지연

대역폭이 문제가 되지 않더라도, 거리가 문제가 될 수 있다. 모든 네트워크 라우터는 인터넷 트래픽을 지연시킨다. 클라이언트와 서버 사이에 라우터가 많지 않아도, 거리에 따라 지연을 유발한다.

적중과 부적중

캐시가 유용하지만, 세상 모든 문서의 사본을 저장하지는 않는다. 캐시에 요청이 도착했을 때, 그에 대응하는 사본이 있다면 요청이 처리될 수 있다. 이것을 캐시 적중cache hit이라고 부른다. 대응하는 사본이 없다면 그냥 원 서버로 전달되기만 한다. 이를 캐시 부적중cache miss라고 부른다.

재검사 (Revalidation)

원 서버 콘텐츠는 변경될 수 있어, 캐시는 반드시 그들이 갖고 있는 사본이 최신인지 서버를 통해 때때로 점검하여야한다. 이 검사를 HTTP 재검사라고 부른다.

캐시는 언제든지 사본을 재검사할 수 있다. 하지만 캐시는 문서를 수백만 개씩 가지고 있는 경우가 흔한데 비해 네트워크 대역폭은 부족해서, 대부분의 캐시는 클라이언트가 가본을 요청하였고, 그 사본이 검사할 필요가 있을 정도로 오래된 경우에만 재검사를 한다.

캐시는 사본의 재검사가 필요할 때, 원 서버에 작은 재검사 요청을 보낸다. 콘텐츠가 변경되지 않았다면, 서버는 작은 304 Not Modified 응답을 보낸다. 이를 재검사 적중 혹은 느린 적중이라 한다. 이것은 순수 캐시 적중보다 느리다. 왜냐하면 서버와 검사를 해야하기 때문이다. 하지만 캐시 부적중 보다는 빠른데, 서버로부터 객체 데이터를 받아올 필요가 없기 때문이다.

재검사를 할때, 콘텐츠가 변경되었다면, 서버는 콘텐츠 전체와 함께 HTTP 200 OK 응답을 클라이언트에게 보낸다. 만약 서버 객체가 삭제되었다면, HTTP 404 Not Found 응답을 돌려보내면서 캐시는 사본을 삭제한다.

적중률

바이트 적중률

바이트 단위 적중률은 캐시를 통해 모든 바이트의 비율을 표현한다. 이 측정값은 트래픽이 절감된 정도를 포착해낸다.

적중과 부적중의 구별

HTTP는 클라이언트에게 응답이 캐시 적중이었는지 서버 접근인지 말해주는 방법을 제공하지 않는다. 두 경우 모두 응답 코드는 응답이 본문을 갖고있다는 뜻의 200 OK가 된다. 어떤 프락시 캐시는 캐시에 무슨 일이 일어났는지 설명하기 위해 Via 헤더에 추가 정보를 붙인다.

클라이언트가 응답이 캐시에서 왔는지 알아내는 방법은 Date 헤더를 이용하는 것이다. 응답의 Date 값을 현재 시각과 비교하여, 응답의 생성일이 더 오래되었다면 클라이언트는 응답이 캐시된 것임을 알아낼 수 있다. 또한 응답이 얼마나 오래되었는지를 말해주는 Age 헤더를 이용하는 방법도 있다.

캐시 토폴로지

캐시가 한 명에게만 할당된 것을 개인 전용 캐시 private cache라 부른다. 개인 전용 캐시는 한 명의 사용자가 자주 찾는 페이지를 담는다.

공유된 캐시는 공용 캐시 public cache라고 불린다. 공용 캐시는 사용자 집단에게 자주 쓰이는 페이지를 담는다.

개인 전용 캐시

개인 전용 캐시는 많은 저장 공간이나 에너지를 필요로 하지 않는다. 웹 브라우저는 개인 전용 캐시를 가지고 있다. 주로 개인용 컴퓨터 디스크와 메모리에 캐시해 놓고 사용자가 캐시 사이즈와 설정을 수정할 수 있도록 허용한다. about:cache를 통해 연결되는 페이지에서 캐시 콘텐츠의 목록을 볼 수 있다.

공용 프락시 캐시

공용 캐시는 캐시 프락시 서버 또는 프락시 캐시라고 불리는 종류의 공유된 프락시 서버이다. 프락시 캐시는 로컬 캐시에서 문서를 제공하거나, 사용자의 입장에서 서버에 접근한다. 공용 캐시에는 여러 사용자가 접근하기 때문에, 불필요한 트래픽을 줄일 수 있다.

클라이언트는 새로운 문서(개인 전용 캐시에 들어있지 않은)에 접근한다. 개인 전용 캐시는 이 문서를 네트워크를 거쳐 가져온다. 이를 만약 공용 캐시를 사용하는 서버라면, 공용 캐시는 자주 찾는 객체를 단 한 번만 가져와 모든 요청에 대해 공유된 사본을 제공함으로써 네트워크 트래픽을 줄인다.

프락시 캐시 계층

작은 캐시에서 캐시 부적중이 발생했을 때, 더 큰 부모 캐시가 그 트래픽을 처리하도록 하는 계층을 만드는 방식이 합리적이다. 클라이언트 주위에는 작고 저렴한 캐시를 사용하고, 계층 상단에는 많은 사용자들에 의해 공유되는 문서를 유지하기 위해 더 크고 강력한 캐시를 사용하는 것이다.

캐시 계층이 깊다면 요청은 캐시의 긴 연쇄를 따라가게 될 것이다. 프락시 연쇄가 길어질수록 각 중간 프락시는 현저한 성능 저하가 발생할 것이다.

캐시망, 콘텐츠 라우팅, 피어링

어떤 네트워크 아키텍처는 복잡한 캐시망을 만든다. 캐시망의 프락시 캐시는 어떤 부모 캐시와 대화할 것인지, 아니면 요청이 캐시를 완전히 우회하여 서버로 바로 가도록 할 것인지에 대한 캐시 커뮤니케이션 결정을 동적으로 내린다.

캐시망 안에서 콘텐츠 라우팅을 위해 설계된 캐시들이 하는 일은 다음과 같다.

  • URL에 근거, 부모 캐시와 서버 중 하나를 동적으로 선택
  • URL에 근거, 특정 부모 캐시를 동적으로 선택
  • 부모 캐시에게 가기 전, 캐시된 사본을 로컬에서 찾는다
  • 다른 캐시들이 그들의 캐시된 콘텐츠에 부분적으로 접근할 수 있도록 허용하되, 그들의 캐시를 통한 인터넷 트랜짓은 허용하지 않는다

캐시 처리 단계

  1. 요청 받기 : 캐시는 네트워크로부터 도착한 요청 메시지를 읽는다.
  2. 파싱 : 캐시는 메시지를 파싱하여 URL과 헤더들을 추출한다.
  3. 검색 : 캐시는 로컬 복사본이 있는지 검사, 없다면 사본을 받아온다(저장)
  4. 검사 : 캐시는 캐시된 사본이 최신인지 확인, 옛날이라면 변경사항을 서버에 물어봄
  5. 응답 생성 : 캐시는 새로운 헤더와 캐시된 본문으로 응답 메시지를 만듬
  6. 발송 : 캐시는 네트워크를 통해 응답을 클라이언트에게 돌려준다.
  7. 로깅 : 선택적으로 캐시는 로그파일에 트랜잭션에 대해 서술한 로그 하나를 남긴다.

사본을 신선하게 유지

캐시된 사본 전체가 서버의 문서와 항상 일치하는 것은 아니다. 캐시된 데이터는 서버의 데이터와 일치하도록 관리되어야 한다.

HTTP는 어떤 캐시가 사본을 갖고 있는지 서버가 기억하지 않더라도 캐시된 사본이 서버와 충분히 일치하도록 유지할 수 있게 해주는 메커니즘을 가지고 있다. 이것을 문서 만료와 서버 재검사라고 한다.

문서 만료

HTTP는 Cache-Control, Expires라는 특별한 헤더들을 이용하여 원 서버가 각 문서에 유효기간을 붙일 수 있게 해준다.

HTTP/1.0 200 OK
Date: Sat, 29 Jun 2002, 14:30:00 GMT
Content-type: text/plain
Content-length: 67
Expires: Fri, 05 Jul 2002, 05:00:00 GMT

Independence Day sale at Joe's Hardware
Come shop with us today!

위 예시는 Expires 헤더를 사용하여 캐시 문서의 유효기간을 표시한 것이다.

HTTP/1.0 200 OK
Date: Sat, 29 Jun 2002, 14:30:00 GMT
Content-type: text/plain
Content-length: 67
Cache-Control: max-age=484200

Independence Day sale at Joe's Hardware
Come shop with us today!

위 예시는 Cache-Control: max-age 헤더를 이용하여 캐시 문서의 유효기간을 표시한 것이다.

캐시 문서가 만료되기 전이면, 캐시는 필요하다면 서버와의 접촉 없이 사본을 제공할 수 있다. 그러나 일단 캐시된 문서가 만료되면 캐시는 반드시 서버와 문서에 변경된 것이 있는지 검사해야 하며, 만약 그렇다면 업데이트된 사본을 얻어와야한다.

유효기간과 나이

서버는 응답 본문과 함께 하는 HTTP/1.0+ ExpiresHTTP/1.1 Cache-Control: max-age 응답 헤더를 이용해서 유효기간을 명시한다. ExpiresCache-Control: max-age 헤더는 기본적으로 같은 일을 한다.

  • Cache-Control: max-age : 문서의 최대의 나이를 정의한다. 최대 나이는 문서가 처음 생성된 이후부터 경과한 시간의 합법적인 최댓값이다.

  • Expires : 절대 유효기간을 명시한다. 만약 유효기간이 경과했다면, 그 문서는 더 이상 최신이 아니다.

서버 재검사

캐시된 문서가 만료되었다는 것은 그 문서가 서버에 현재 존재하는 것과 실제로 다르다는 것을 의미하는게 아니라, 검사할 시간이 되었음을 뜻한다.

이 검사를 캐시가 서버에게 문서가 변경되었는지의 여부를 물어볼 필요가 있음을 의미하는 서버 재검사라고 한다.

  • 재검사 결과 콘텐츠가 변경되었다면, 캐시는 그 문서의 새로운 사본을 가져와 오래된 데이터 대신 저장한 뒤 클라이언트에게 보내준다.
  • 재검사 결과 콘텐츠가 변경되지 않았다면 캐시는 새 만료일을 포함한 새 헤더만 가져와서 캐시 안의 헤더들을 갱신한다.

이는 서버 트래픽을 절약하고 사용자 응답 시간을 개선하는 좋은 방법이다.

HTTP 프로토콜은 캐시가 다음 중 하나를 반환할 것을 요구한다.

  • 나름 최신의 캐시된 사본
  • 서버와 재검사되었기 때문에, 최신 문서라고 확신할 수 있는 캐시된 사본
  • 에러 메시지(재검사해야하는 서버 다운시)
  • 경고 메시지가 부착된 캐시된 사본(부적중)

조건부 메서드와의 재검사

HTTP 조건부 메서드는 재검사를 효율적으로 만들어준다. HTTP는 캐시가 서버에게 조건부 GET 요청을 보낼 수 있도록 한다. 이 요청은 서버가 가지고 있는 문서가 캐시가 가지고 있는 문서와 다른 경우에만 객체 본문을 보내달라고 하는 것이다.

이렇게 캐시 문서의 최신여부 검사 및 객체를 받아오는 것은 조건부 GET으로 결합된다. 웹 서버는 조건이 참인 경우에만 객체를 반환한다.

HTTP는 다섯 가지 조건부 요청 헤더를 정의한다. 그 중 두 가지는 캐시 재검사를 할 때 가장 유용한 If-Modified-Since, If-None-Match이다. 모든 조건부 헤더는 If접두어로 시작한다.

If-Modified-Since: 날짜 재검사

가장 흔히 쓰이는 캐시 재검사 헤더이다. If-Modified-Since 재검사 요청은 흔히 IMS 요청으로 불린다. IMS 요청은 서버에게 리소스가 특정 날짜 이후로 변경된 경우에만 요청한 본문을 보내달라고 한다.

  • 문서가 주어진 날짜 이후에 변경되었다면, If-Modified-Since 조건은 참이고, GET 요청은 성공한다. 그리고 새 문서가 새로운 만료 날짜와 그 외 다른 정보들이 담긴 헤더들과 함께 캐시에게 반환된다.
  • 문서가 주어진 날짜 이후에 변경되지 않았다면 조건은 거짓이고, 서버는 304 Not Modified 응답 메시지를 클라이언트에게 돌려준다.

If-None-Match: 엔티티 태그 재검사

문서를 변경했을 때, 문서의 엔티티 태그를 새로운 버전으로 표현할 수 있다. 엔티티 태그가 변경되었다면, 캐시는 새 문저의 사본을 얻기 위해 If-None-Match 조건부 헤더를 사용할 수 있다.

검사기

서버가 엔티티 태그를 반환시, 클라이언트는 엔티티 태그 검사기를 사용해야하고, Last-Modified 반환시, 클라이언트는 If-Modified-Since 검사를 사용할 수 있다. 엔티티 태그 검사기는 강한 검사기이고, 조금이라도 바뀌면 새로운 사본을 받아올 수 있도록 해야한다.

캐시 제어

HTTP는 문서가 만료되기 전까지 얼마나 오랫동안 캐시될 수 있게 할 것인지 서버가 설정할 수 있는 여러 가지 방법을 정의한다.

  • Cache-Control: no-store 헤더를 응답에 첨부
  • Cache-Control: no-cache 헤더를 응답에 첨부
  • Cache-Control: must-revalidate 헤더를 응답에 첨부
  • Cache-Control: max-age 헤더를 응답에 첨부
  • Expires 날짜 헤더를 응답에 첨부
  • 캐시가 스스로 휴리스틱 방법으로 결정

HTTP/1.1은 신선도를 관리하기 위해 객체를 캐시하는 것을 제한하거나 캐시된 객체를 제공하는 여러가지 방법을 제공한다.

Cache-Control: no-store
Cache-Control: no-cache
Pragma: no-cache

no-store : 캐시가 그 응답의 사본을 만드는 것을 금지한다. 이 응답을 전달하고 나면 객체를 삭제할 것이다.

no-cache : 로컬 캐시 저장소에 저장될 수 있다. 먼저 서버와 재검사를 하지 않고서는 캐시에서 클라이언트로 제공될 수 없다.

Pragma: no-cache : HTTP/1.0+ 와 하위호환성을 위해 HTTP/1.1에 포함되어있다.

Cache-Control: max-age=3600
Cache-Control: s-maxage=3600

Cache-Control: max-age 헤더는 신선하고 간주되었던 문서가 서버로부터 온 이후로 흐른 시간을 초로 나타낸다. 또한 s-maxage 헤더는 max-age처럼 행동하지만 공용 캐시에만 적용된다.

서버는 maximum aging을 0으로 설정함으로써, 캐시가 매 접근마다 문서를 캐시하거나 리프레시하지 않도록 요청할 수 있다.

Expires: Fri, 05 Jul 2002, 05:00:00 GMT

deprecatedExpires 헤더는 초 단위의 시간 대신 실제 만료 날짜를 명시한다.

Cache-Control: must-revalidate

Cache-Control: must-revalidate : 캐시는 성능을 개선하기 위해 만료된 객체를 제공하도록 설정될 수 있다. 이 응답 헤더는 캐시가 이 객체의 만료된 사본을 원 서버와의 최초 재검사 없이는 제공해서는 안됨을 의미한다.

휴리스틱 만료

만약 응답에서 Cache-Control: max-age 헤더나 Expires 헤더가 없다면, 캐시는 경험적인 방법으로(heuristic) 최대 나이를 계산한다. 만약 계산 결과 얻은 최대 나이 값이 24시간보다 크면 Heuristic Expiration 경고 헤더가 응답 헤더에 추가되어야 한다. 이 경고헤더는 클라이언트가 대부분 볼 수 없다.

유명한 휴리스틱(heuristic) 만료 알고리즘의 하나인 LM 인자 알고리즘은 문서가 최근 변경 일시를 포함하고 있다면 사용할 수 있다. LM 인자 알고리즘은 최근 변경 일시를 문서가 얼마나 자주 바뀌는지에 대한 추청에 사용한다.

  • 캐시된 문서가 마지막으로 변경된 것이 옛날이라면, 안정적인 문서일 것이고 바뀔 가능성이 크지 않을 것이다. 즉 캐시에 더 오래 보관하고 있어도 안전하다.
  • 캐시된 문서가 최근에 변경되었다면, 자주 변경되는 문서일 것이므로 서버와 재검사하기 전까지 짧은 기간 동안만 캐시해야 한다.