캐시의 원리와 제어방법을 알아본다.
클라가 요청 메시지를 받은 이후의 처리단계를 기준으로 캐시는 어떤 원리로 사본을 관리하는지, 단계별로 어떤 제어가 가능한지를 알아보려고 한다.
7. 캐시 처리 단계
처리단계는 7단계로 이루어져 있다.
단계1: 요청받기
네트워크 커넥션에서의 활동을 감지하고,
들어오는 데이터를 읽어들인다.
단계2: 파싱
요청 메세지를 여러 부분으로 파싱하여
헤더 부분을 조작하기 쉬운 자료 구조에 담는다.
캐싱 소프트웨어가 헤더 필드를 처리하고 조작하기 쉽게 만들어 준다.
단계3: 검색
URL을 알아내고 그에 해당하는 로컬 사본이 있는지 검사한다.
메모리에 저장되어있을 수도 있고,
디스크나
근처 다른 컴퓨터에 있을 수 있다.문서를 로컬에서 가져올 수 없다면, 상황이나 설정에 따라서
원서버나 부모프락시에서 가져오거나
혹은 실패를 반환한다.캐시 된 객체는 서버 응답 본문과 원서버 응답 헤더를 포함하므로
캐시 적중 동안 올바른 서버 헤더가 반환될 수 있다.객체가 얼마나 오랫동안 캐시에 머무르고 있었는지 알려주는 기록이나
얼마나 자주 사용되었는지 등에 대한
몇몇 메타데이터를 포함한다.
단계4: 신선도 검사
HTTP는 캐시가 일정 기간 동안 서버 문서의 사본을 보유할 수 있도록 해준다.
- 이 기간 동안 문서는 신선한 것으로 간주되고,
캐시는 서버와의 접촉 없이 이 문서를 제공할 수 있다. - 너무 오래 갖고 있었다면 그 객체는 신선하지 않은 것으로 간주
캐시는 그 문서를 제공하기 전에 문서에 어떤 변경이 있었는지 검사하기 위해 서버와 재검사를 해야 한다. (304 Not Modified
)
신선도 검사 알고리즘은 아래에.
단계5: 응답 생성
캐시 된 응답을 원서버에서 온 것처럼 보이게 하고 싶기 때문에
캐시는 캐시 된 서버 응답 헤더를 토대로 응답 헤더를 생성한다.
- 이 기저 헤더들은 캐시에 의해 수정되고 늘어난다.
캐시는 클라에 맞게 이 헤더를 조정해야 하는 책임이 있다.
- 캐시는 캐시 신선도 정보를 삽입하며
(Cache-Control
,Age
,Expires
헤더) - 요청이 프락시 캐시를 거쳐갔을 경우
Via
헤더를 포함시킨다. - Date 헤더는 그 객체가 원서버에서 최초로 생겨난 일시이기 때문에 조정해서는 안된다.
단계6: 전송
단계7: 로깅
대부분의 캐시는 로그 파일과 캐시 사용 통계를 유지한다.
- 각 캐시 트랜잭션이 완료된 후, 캐시는 통계 캐시 적중과 부적중 횟수에 대한 통계를 갱신한다.
- 로그 파일에 요청 종류, URL 무엇이 일어났는지 알려주는 항목을 추가한다.
캐시 로그 포맷
- 스퀴드 로그 포맷 Squid log format 참고
- 넷스케이프 확장 공용 로그 포맷 Netscape extended common log format
많은 캐시 제품이 커스텀 로그 파일을 허용한다.
전체 Flow
8. 사본을 신선하게 유지하기
HTTP는 어떤 캐시가 사본을 갖고 있는지 서버가 기억하지 않더라도,
캐시 된 사본이 서버와 충분히 일치하도록 유지할 수 있게 해주는 매커니즘을 갖고 있다.
매커니즘은 문서 만료와 서버 재검사라고 부른다.
8.1 문서만료
HTTP는 Cache-Control
과 Expires
라는 헤더들을 이용하여
원서버가 각 문서에 유효기간을 붙일 수 있게 해준다.
- 이 헤더들은 콘텐츠가 얼마나 오랫동안 신선한 상태로 보일 수 있는지 좌우한다.
캐시 문서가 만료되기 전에,
캐시는 필요하다면 서버와의 접촉 없이 사본을 제공할 수 있다.캐시 된 문서가 만료되면,
캐시는 반드시 서버와 문서에 변경된 것이 있는지 검사해야 하며, (변경이 없으면304 Not Modified
)만약 그렇다면 신선한 사본을 얻어와야 한다. (새 유효기간과 함께)
유효기간과 나이
Expires
와 Cache-Control:max-age
헤더는 기본적으로 같은 일을 하지만,
절대 시간은 컴퓨터의 시계가 올바르게 맞추어져 있을 것을 요구한다.
Cache-Control:max-age
- max-age 값은 문서의 최대 나이를 정의한다.
- 최대 나이는
문서가 처음 생성된 이후부터,
제공하기엔 더 이상 신선하지 않다고 간주될 때까지
경과한 시간의 합법적인 최댓값(초단위)이다. - 현재시간과 expires 시간의 남은 시간을 나이로 측정한다.
Expires
절대 유효기간을 명시한다.
만약 유효기간이 경과했다면, 그 문서는 더 이상 신선하지 않다.
8.2 서버 재검사 reValidation
캐시 된 문서가 만료되었다는 것은,
그 문서가 원서버에 현재 존재하는 것과 실제로 다르다는 것을 의미하지는 않으며,
이제 검사할 시간이 되었음을 뜻한다.캐시가 원서버에게 문서가 변경되었는지의 여부를 묻는 것
콘텐츠가 변경되었다면:
- 캐시는 그 문서의 새로운 사본을 가져와
- 새로 저장한 뒤
- 클라에게도 보낸다.
콘텐츠가 변경되지 않았다면:
- 캐시는 새 만료일을 포함한 새 헤더들만 가져와서
- 캐시 안의 헤더들을 갱신한다.
캐시는 문서의 신선도를 매 요청마다 검증할 필요가 없다.
- 문서가 만료되었을 때, 한 번만 서버와 재검사하면 된다.
- 신선하지 않은 콘텐츠는 제공하지 않으면서도,
서버의 트래픽을 절약하고 사용자 응답 시간을 개선한다.
HTTP 프로토콜은 캐시가 다음 중 하나를 반환할 것을 요구한다.
- 충분히 신선한 캐시 된 사본
- 원서버와 재검사되었기 때문에, 충분히 신선하다고 확신할 수 있는 캐시 된 사본
- 에러메시지 (재검사헤야 하는 원서버가 죽을 경우)
- 경고 메세지가 부착된 캐시 된 사본(부정확시)
조건부 메서드와 재검사
HTTP의 조건부 메서드는 재검사를 효율적으로 만들어준다.
HTTP는 캐시가 서버에게 조건부 GET이라는 요청을 보낼 수 있도록 해준다.
- 이 요청은 서버가 갖고 있는 문서가 캐시가 갖고 있는 것과 다를 경우에만
객체 본문을 보내달라고 하는 것이다. - 신선도 검사와 객체를 받아오는 것은 하나의 조건부 GET으로 결합된다.
- 조건부 GET은 GET 요청 메세지에 특별한 조건부 헤더를 추가함으로써 시작된다.
- 웹 서버는 조건이 참인 경우에만 객체를 반환한다.
- HTTP는 캐시 재검사 시 가장 유용한 2가지 조건부 요청 헤더를 정의한다.
- If-Modified-Since
- If-None-Match
1. If-Modified-Since
: 날짜 재검사
1 | If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT |
줄여서 IMS 요청으로 불린다.
서버에게 리소스가 특정 날짜 이후로 변경된 경우에만 요청한 본문을 보내달라고 한다.
만약 문서가 주어진 날짜 이후에 변경되지 않았다면, 조건은(If-Modified-Since) 거짓이고
서버는304 Not Modified
응답메세지를 클라에게 돌려준다.효율을 위해 본문은 보내지 않는다.
응답 헤더들을 포함하지만, 원래 돌려줘야 할 것에서 갱신이 필요한 것만을 보내준다. (보통 새 만료 날짜만 보내준다.)
If-Modified-Since 헤더는 Last-Modified 헤더와 함께 동작한다.
원서버는 제공하는 문서에 최근 변경 일시를 붙인다.
마지막 배포 시간이 Last-Modified로 내려온다.
캐시가 캐시 된 문서를 재검사하려고 할때,
캐시 된 사본이 마지막으로 수정된 날짜가 담긴 If-Modified-Since헤더를 포함한다.1
If-Modified-Since: <캐시 된 마지막 수정일>
몇몇 웹서버는 If-Modified-Since를 실제 날짜 비교로 구현하지 않는다.
대신 IMS 날짜와 최근 변경일 간의 문자열 비교를 수행한다.
- 정확히 이 날짜에 마지막 변경이 일어난 것이 아니라면 이라는 의미로 동작한다.
GET 또는 HEAD에서만 사용 가능하다.
일반적인 사용예) ETag가 없는 캐시 된 엔티티로 업데이트한다.
If-None-Match와 함께 사용 시 무시된다.
2. If-None-Match
: 엔터티 태그 재검사
서버는 무서에 대한 일련번호와 같이 동작하는 특별한 태그를 제공할 수 있다.(ETag)
캐시 된 태그가 서버에 있는 문서의 태그와 다를 때만 요청을 처리한다.
최근 변경일시 재검사가 행해지기 어려운 상황이 있다.
- 일정 시간 간격으로 다시 쓰여지지만, 같은 데이터를 포함한다. (내용변화가 없음)
- 어떤 문서들의 변경은 다시 읽어들이기엔 사소한 것 (철자나 주석)
- 어떤 서버들은 그들이 갖고 있는 페이지에 대한 최근 변경 일시를 정확하게 판별할 수 없다.
- 1초보다 작은 간격으로 갱신되는 문서를 제공하는 서버들에게는,
변경일에 대한 1초의 정밀도는 충분하지 않을 수 있다.
1 | 조건부 요청 |
1 | 응답 |
- 만약 서버의 엔터티 태그가 변경되었다면(v3.0)
서버는 200 OK 응답으로 새 콘텐츠를 새 ETag와 함께 반환했을 것이다.
약한 검사기와 강한 검사기
- 캐시는 캐시 된 버전이 서버가 갖고 있는 것에 대해 최신인지 확인하기 위해 엔터티 태그를 사용한다.
- 엔터티 태그와 최근 변경일시는 둘 다 캐시 검사기다.
- 서버는 때때로 모든 캐시 된 사본을 무효화시키지 않고,
문서를 살짝 고칠 수 있도록 허용하고 싶은 경우가 있다. - HTTP/1.1은 콘텐츠가 조금 변경되었더라도, 그 정도면 같은 것이라고 서버가 주장할 수 있도록 해주는 약한 검사기를 지원한다.
강한 검사기는 콘텐츠가 바뀔 때마다 바뀐다.
약한 검사기는 어느 정도 콘텐츠 변경을 허용하지만, 콘텐츠의 중요한 의미가 변경되면 함께 변경된다.
- 서버는
W/
접두사로 약한 검사기를 구분한다.
1 | ETag: W/"v2.6" |
언제 엔터티 태그를 사용하고 언제 Last-Modified 일시를 사용하는가
- HTTP/1.1 클라는 만약 서버가 엔터티 태그를 반환했다면,
반드시 엔터티 태그 검사기를 사용해야 한다.
- HTTP/1.1 클라는 만약 서버가 엔터티 태그를 반환했다면,
- Last-Modified 값만을 반환했다면
클라는 If-Modified-Since 검사를 사용할 수 있다. - 만약 HTTP/1.1 캐시나 서버가 If-Modified-Since와 엔터티 태그, 조건부 헤더를 모두 받았다면,
요청의 모든 조건부 헤더 필드의 조건에 부합해야 200을 반환해야 한다.
9. 캐시 제어
아래는 HTTP는 문서가 얼마나 오랫동안 캐시될 수 있게 할 것인지,
서버가 설정할 수 있는 여러 가지 방법이다.
Cache-control: no-cache
Cache-control: no-store
Cache-control: must-revalidate
Cache-Control: max-age=<seconds>
Cache-control: s-maxage=<seconds>
Expires
날짜 헤더를 응답에 첨부할 수 있다.- 아무 만료 정보도 주지 않고,
캐시가 스스로 체험적인(휴리스틱 heuristic) 방법으로 결정하게 할 수 있다.
9.1 no-cahce와 no-store 응답 헤더
캐시가 검증되지 않은 캐시 된 객체로 응답하는 것을 막는다.
Cache-control: no-store
: 캐시가 그 응답의 사본을 만드는 것을 금지한다.Cache-control: no-cache
: 로컬 캐시 저장소에 저장될 수 있다. 다만, 먼저 서버와 재검사를 하지 않고서는 캐시에서 클라로 제공될 수 없을 뿐이다.Do Not Serve From Cache Without Revalidation (재검사 없이 캐시에서 제공하지 마라)
Pragma: no-cache
: HTTP/1.0+ 하위 호환성을 위해 HTTP/1.1에 포함되어있다. 웬만하면Cache-control: no-store
9.2 Max-Age 응답 헤더
max-age
신선하다고 간주되었던 문서가 서버로부터 온 이후로 흐른 시간- 초로 나타낸다.
s-maxage
헤더는 공유된 캐시에만 적용된다.- 서버는 maximum aging(최대 나이먹음)을 0으로 설정함으로써,
캐시가 매 접근마다 문서를 캐시하거나 리프레시 하지 않도록 요청할 수 있다.
9.3 Expires 응답헤더
- 더 이상 사용하지 않기를 권하는 Expires 헤더는 실제 만료 날짜를 명시한다.
- HTTP 설계자들은 많은 서버가 동기화되어있지 않거나
부정확한 시계를 갖고 있기 때문에,
만료를 절대 시각 대신 경과된 시간으로 표현하는 것이 낫다고 판단했다.
- HTTP 설계자들은 많은 서버가 동기화되어있지 않거나
- 신선도 수명의 근삿값은 만료일과 생성일의 초 단위 시간차를 계산하여 얻을 수 있다.
9.4 Must-Revalidate 응답 헤더
- 캐시는 성능을 개선하기 위해 신선하지 않은(만료된) 객체를 제공하도록 설정될 수 있다.
- 만약 캐시가 만료 정보를 엄격하게 따르길 원한다면 must-revalidate 응답 헤더
- 캐시가 이 객체의 신선하지 않은 사본을
원서버와의 최초의 재검사 없이는 제공해서는 안됨을 의미한다. - 만약 캐시가 must-revalidate 신선도 검사를 시도했을 때 원서버가 사용할 수 없는 상태라면
캐시는 반드시504 Gateway Timeout error
를 반환해야 한다.
9.5 휴리스틱 만료
- 만약 응답이 Cache-control:max-age 헤더나 expires 헤더들을 포함하지 않고 있다면
캐시는 경험적인 방법으로 heuristic 최대 나이를 게산할 것이다. - 계산 결과, 얻은 최대 나이 값이 24시간보다 크다면,
Heuristic Expiration 경고(경고13) 헤더가 응답 헤더에 추가되어야 한다.- 이 경고 정보를 사용자가 볼 수 있게 해주는 브라우저는 없다.
LM(last-modified) 인자 알고리즘 (휴리스틱 만료 알고리즘)
- 문서가 최근 변경일시를 포함하고 있다면 사용할 수 있다.
- LM 인자 알고리즘은 최근 변경일시를 문서가 얼마나 자주 바뀌는지에 대한 추정에 사용한다.
- 캐시 된 문서가 마지막으로 변경된 것이 엄청 예전이라면,
=> 안정적인 문서일 것이다
=> 갑자기 바뀔 일을 크지 않을 것이고
=> 캐시에 더 오래 보관하고 있어도 안전하다. - 캐시 된 문서가 최근 변경되었다면
=> 자주 변경될 것이고,
=> 서버와 재검사하기 전까지 짧은 기간 동안만 캐시해야 한다.
1 | $마지막_수정이후로_경과한_시간 = max(0, $서버의_Date - $서버의_Last_Modified) |
- 일반적으로 사람들은 휴리스틱 신선도 유지 기간에 상한을 설정하여
지나치게 커지는 것을 막는다. - 보통 1주일로 하지만, 보수적인 사이트는 하루도 설정한다.
- 캐시는 일반적으로 신선도에 대한 아무런 단서가 없는 문서에 대해 기본 신선도 유 지기간을 설정한다.
보통 한 시간이나 하루로. - 더 보수적인 캐시는 보통 이 휴리스틱 문서들에 대해 0의 신선도 수명을 설정하여
캐시가 클라에게 데이터를 제공할 때마다 아직 신선한지 검사하도록 강제한다.
9.6 클라 신선도 제약
- 웹브라우저는 브라우저나 프락시 캐시의 신선하지 않은 콘텐츠를 강제로 갱신시켜주는 리프레시나 리로드 버튼을 갖고 있다.
- 리프레시 버튼은 Cache-control 요청 헤더가 추가된 GET 요청을 발생시켜서,
강제로 재검사하거나 서버로부터 콘텐츠를 무조건 가져온다.
클라는 Cache-control 요청 헤더를 사용하여 만료 제약을 엄격하게 하거나, 느슨하게 할 수 있다.
- 성능, 신뢰성, 비용 개선을 위한 절충안으로 신선도 요구 사항을 느슨하게 하고자 할 수 있다.
Cache-Control: max-stale[=<seconds>]
: 클라이언트가 캐시의 만료 시간을 초과한 응답을 받아들일지를 나타낸다. 초가 지정되면, 클라는 만료시간이 그 매개변수 값만큼 지난 문서도 받아들인다.Cache-Control: min-fresh=<seconds>
: 클라는 지금으로부터 s초 후까지 신선한 문서만을 받아들인다.Cache-Control: max-age=<seconds>
: s초보다 오랫동안 캐시 된 문서를 반환할 수 없다.Cache-control: no-cache
: 캐시 된 리소스는 재검사하기 전에는 받아들이지 않을 것이다.Cache-control: no-store
: 저장소에서 문서의 흔적을 빨리 삭제해야 한다. 문서에는 민감한 정보가 포함되어 있기 때문Cache-control: only-if-cached
: 클라는 캐시에 들어있는 사본만을 원한다.
10. 자세한 알고리즘
나이와 신선도 수명
- 캐시 된 사본의 나이가 신선도 수명보다 작으면 사본은 제공해주기에 충분히 신선한 것이다.
1
$충분히_신선한가 = $나이 < $신선도_수명
- 문서의 나이: 서버가 문서를 보낸 후(혹은 서버가 마지막으로 재검사한 후) 그 문서가 나이를 먹은 시간의 총합이다.
- 캐시는 문서 응답이 어떻게 왔는지 모르기 때문에, 문서가 완전히 새롭다고 가정하지 못한다.
Age
헤더를 통해 명시적으로든
서버가 생성한 Date 헤더를 통해 계산하든 간에
문서의 나이를 판별해야 한다.
10.1 나이 계산
- 응답의 나이는 응답이 서버에서 생성되었을 때부터 지금까지의 총 시간이다.
- 응답이 인터넷상의 라우터들과 게이트웨이들 사이를 떠돌아다닌 시간과
응답이 캐시에 머물렀던 시간을 포함한다.
1 | $겉보기_나이 = max(0, $응답을_받은_시각 - $Date_헤더값) |
1. 겉보기 나이는 Date 헤더에 기반한다.
- 모든 컴퓨터가 똑같이 정확한 시계를 갖고 있다면,
캐시 된 문서의 나이는 단순히
현재 시간에서 서버가 문서를 보낸 시간(Date 헤더)을 뺀 겉보기 나이가 될 것이다.1
$겉보기_나이 = $응답을_받은_시각 - $Date_헤더값
- 모든 컴퓨터는 똑같이 정확한 시계를 갖고 있지 않다.
클록 스큐 clock skew: 두 컴퓨터의 시계 설정 차이로 인한 문제
클록 스큐 때문에 겉보기 나이는 종종 부정확하며 때로는 음수가 되기도 한다. - 겉보기 나이가 음수가 될 경우 0으로 만들어야 한다.
1
$겉보기_나이 = max(0, $응답을_받은_시각 - $Date_헤더값)
2. 점층적 나이 계산 (Age 헤더)
- 문서가 프락시나 캐시를 통과할 때마다
그 장치들이 Age헤더에 상대적인 나이를 누적해서 더하도록 한다. - 이 방법은 서버 같의 시간 비교나, 종단 간의 시간 비교를 필요로 하지 않는다.
Age 헤더 값은 문서가 프락시들을 통과하면서 점점 늘어난다.
- HTTP/1.1을 이해하는 어플리케이션은 문서가 각 어플리케이션에 머무른 시간과 네트워크 사이를 이동한 시간만큼 Age 헤더의 값을 늘려야 한다.
- 각 중간 어플리케이션은 자신의 내부 시계를 이용해서 쉽게 문서의 체류 시간을 계산할 수 있다.
- 비-HTTP/1.1 장치는 Age 헤더를 인식하지 못한다.
상대 나이값은 가장 큰 것(보수적)이 선택된다.
- 다른 서버에서 넘어온 Date 값이나 나이계산값은 실제보다 작게 계산된 값일 수 있기 때문이다.
1 | $겉보기_나이 = max(0, $응답을_받은_시각 - $Date_헤더값) |
3. 네트워크 지연에 대한 보상
- 느린 네트워크나 과부하가 걸린 서버에서 => 문서가 네트워크나 서버의 교통 혼잡에 긴 시간 동안 갇혀있었던 경우 상대 나이 계산은 문서의 나이에 대한 상당히 모자란 추정이 될 수 있다.
- Date헤더는 문서가 캐시로 옮겨가는 도중에 얼마나 시간을 소비했는지 말해주지 않는다.
1
2
3
4$겉보기_나이 = max(0, $응답을_받은_시각 - $Date_헤더값)
$보정된_겉보이_나이 = max($겉보기_나이, $Age_헤더값)
$응답_지연_추정값 = $응답을_받은_시각 - $요청을_보낸_시각;
$문서가_우리의_캐시에_도착했을_때의_나이 = $보정된_겉보기_나이 + $응답_지연_추정값;
4. 캐시에 저장되면, 나이를 더 먹는다.
- 문서에 대한 요청이 캐시에 도착했을 때,
우리는 그 문서의 현재 나이를 계산하기 위해 그 문서가 캐시에 얼마나 오랫동안 머물렀는지 알 필요가 있다.
1 | $나이 = $문서가_우리의_캐시에_도착했을_때의_나이 + $사본이_우리의_캐시에_머무른_시간; |
10.2 신선도 수명 계산
- 신선도 수명은 서버와 클라의 제약조건에 의존한다.
- 서버는 문서가 얼마나 자주 변경되어 발행되는지에 대한 정보를 갖고 있을 수도 있다.
- 클라는 속도가
더 빠르다면 약간 신선하지 못한 콘텐츠도 받아들이려 할 수도 있고
가능한 가장 최신의 콘텐츠를 요구할 수도 있다.
1 | sub 서버_신선도_한계 { |
전체 과정은 문서의 나이와 신선도 한계라는 두 가지 변수가 관련되어 있다.
- HTTP 완벽가이드 책을 보고 이해한 내용을 정리 한 글입니다.
참고자료