国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

聚焦http協(xié)議緩存策略(RFC7234)在okhttp中的實現(xiàn)

sanyang / 1545人閱讀

摘要:計算緩存從產(chǎn)生開始到現(xiàn)在的年齡。響應(yīng)頭中的時間,文件在緩存服務(wù)器中存在的時間。響應(yīng)碼為表示經(jīng)服務(wù)器效驗緩存的響應(yīng)式有效的可以使用,更新緩存年齡。小結(jié)通過本篇我們知道了協(xié)議的緩存策略,已經(jīng)在中是如何實踐的。

前言

分析基于okhttp v3.3.1

Okhttp處理緩存的類主要是兩個CacheIntercepter緩存攔截器,以及CacheStrategy緩存策略。 CacheIntercepter在Response intercept(Chain chain)方法中先得到chain中的request然后在Cache獲取到Response,然后將Request和Respone交給創(chuàng)建CahceStrategy.Factory對象,在對象中得到CacheStrategy。代碼看的更清晰:

@Override public Response intercept(Chain chain) throws IOException { //cache中取Response對象cacheCandidate Response cacheCandidate = cache != null ");

1、 關(guān)于RFC7234在Okhttp中的實現(xiàn)

1.1、 獲取CacheStrategy緩存策略

看下CacheStrategy.Factory使用原始的Request和在緩存中得到的Response對象CacheCandidate,怎樣生成CacheStrategy的。 CacheStrategyFactory的生成

... public Factory(long nowMillis, Request request, Response cacheResponse) { //獲取當(dāng)前時間 this.nowMillis = nowMillis; this.request = request; this.cacheResponse = cacheResponse; if (cacheResponse != null) { this.sentRequestMillis = cacheResponse.sentRequestAtMillis(); this.receivedResponseMillis = cacheResponse.receivedResponseAtMillis(); Headers headers = cacheResponse.headers(); for (int i = 0, size = headers.size(); i < size; i++) { String fieldName = headers.name(i); String value = headers.value(i); if ("Date".equalsIgnoreCase(fieldName)) { //取出緩存響應(yīng)當(dāng)時服務(wù)器的時間 servedDate = HttpDate.parse(value); servedDateString = value; } else if ("Expires".equalsIgnoreCase(fieldName)) { //取出過期時間 expires = HttpDate.parse(value); } else if ("Last-Modified".equalsIgnoreCase(fieldName)) { //取出最后一次更改時間 lastModified = HttpDate.parse(value); lastModifiedString = value; } else if ("ETag".equalsIgnoreCase(fieldName)) { //取出etag etag = value; } else if ("Age".equalsIgnoreCase(fieldName)) { // ageSeconds = HttpHeaders.parseSeconds(value, -1); } } } }

1.2、 CacheStrategy生成,緩存策略的生成。

緩存策略最終會產(chǎn)生三種策略中的一種:

直接使用緩存

不使用緩存

有條件的使用緩存

CacheStrategy中最后request為空表示可以使用緩存,如果Response為空表示不能使用緩存 如果都為空 說明不能使用直接返回504

具體判斷

    判斷本地是否有cacheReponse 如果沒有直接返回new CacheStrategy(request, null)

    判斷https的handshake是否丟失 如果丟失直接返回 return new CacheStrategy(request, null)

    判斷response和request里的cache-controlheader的值如果有no-store直接返回 return new CacheStrategy(request, null);

    如果request的cache-contro 的值為no-cache或者請求字段有“If-Modified-Sine”或者“If-None—Match”(這個時候表示不能直接使用緩存了)直接返回 return new CacheStrategy(request, null); 5.判斷是否過期,過期就帶有條件的請求,未過期直接使用。

源碼上加了注釋

/** Returns a strategy to use assuming the request can use the network. */ private CacheStrategy getCandidate() { // 在緩存中沒有獲取到緩存 if (cacheResponse == null) { return new CacheStrategy(request, null); } // https不滿足的條件下不使用緩存 if (request.isHttps() && cacheResponse.handshake() == null) { return new CacheStrategy(request, null); } //Request和Resonse中不滿足緩存的條件 if (!isCacheable(cacheResponse, request)) { return new CacheStrategy(request, null); } //在header中存在著If-None-Match或者If-Modified-Since的header可以作為效驗,或者Cache-control的值為noCache表示客戶端使用緩存資源的前提必須要經(jīng)過服務(wù)器的效驗。 CacheControl requestCaching = request.cacheControl(); if (requestCaching.noCache() || hasConditions(request)) { return new CacheStrategy(request, null); } //緩存的響應(yīng)式恒定不變的 CacheControl responseCaching = cacheResponse.cacheControl(); if (responseCaching.immutable()) { return new CacheStrategy(null, cacheResponse); } //計算響應(yīng)緩存的年齡 long ageMillis = cacheResponseAge(); //計算保鮮時間 long freshMillis = computeFreshnessLifetime(); //Request中保鮮年齡和CacheResponse中保鮮年齡取小 if (requestCaching.maxAgeSeconds() != -1) { freshMillis = Math.min(freshMillis, SECONDS.toMillis(requestCaching.maxAgeSeconds())); } //取Request的minFresh(他的含義是當(dāng)前的年齡加上這個日期是否還在保質(zhì)期內(nèi)) long minFreshMillis = 0; if (requestCaching.minFreshSeconds() != -1) { minFreshMillis = SECONDS.toMillis(requestCaching.minFreshSeconds()); } //當(dāng)緩存已經(jīng)過期且request表示能接受過期的響應(yīng),過期的時間的限定。 long maxStaleMillis = 0; if (!responseCaching.mustRevalidate() && requestCaching.maxStaleSeconds() != -1) { maxStaleMillis = SECONDS.toMillis(requestCaching.maxStaleSeconds()); } //判斷緩存能否被使用 if (!responseCaching.noCache() && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) { Response.Builder builder = cacheResponse.newBuilder(); //已經(jīng)過期但是能使用 if (ageMillis + minFreshMillis >= freshMillis) { builder.addHeader("Warning", "110 HttpURLConnection "Response is stale""); } //緩存的年齡已經(jīng)超過一天的時間 long oneDayMillis = 24 * 60 * 60 * 1000L; if (ageMillis > oneDayMillis && isFreshnessLifetimeHeuristic()) { builder.addHeader("Warning", "113 HttpURLConnection "Heuristic expiration""); } return new CacheStrategy(null, builder.build()); } // Find a condition to add to the request. If the condition is satisfied, the response body // will not be transmitted. String conditionName; String conditionValue; if (etag != null) { conditionName = "If-None-Match"; conditionValue = etag; } else if (lastModified != null) { conditionName = "If-Modified-Since"; conditionValue = lastModifiedString; } else if (servedDate != null) { conditionName = "If-Modified-Since"; conditionValue = servedDateString; } else { return new CacheStrategy(request, null); // No condition! Make a regular request. } Headers.Builder conditionalRequestHeaders = request.headers().newBuilder(); Internal.instance.addLenient(conditionalRequestHeaders, conditionName, conditionValue); Request conditionalRequest = request.newBuilder() .headers(conditionalRequestHeaders.build()) .build(); return new CacheStrategy(conditionalRequest, cacheResponse); }

1.2.1、 判斷是否過期

判斷是否過期的依據(jù):ageMillis + minFreshMillis < freshMillis + maxStaleMillis,(當(dāng)前緩存的年齡加上期望有效時間)小于(保鮮期加上過期但仍然有效期限)

特別的當(dāng)respone headercache-control:must-revalidate時表示不能使用過期的cache也就是maxStaleMillis=0。

//計算緩存從產(chǎn)生開始到現(xiàn)在的年齡。 long ageMillis = cacheResponseAge(); //計算服務(wù)器指定的保鮮值 long freshMillis = computeFreshnessLifetime(); //請求和響應(yīng)的保鮮值取最小 if (requestCaching.maxAgeSeconds() != -1) { freshMillis = Math.min(freshMillis, SECONDS.toMillis(requestCaching.maxAgeSeconds())); } //期望在指定時間內(nèi)的響應(yīng)仍然有效,這是request的期望 long minFreshMillis = 0; if (requestCaching.minFreshSeconds() != -1) { minFreshMillis = SECONDS.toMillis(requestCaching.minFreshSeconds()); } //接受已經(jīng)過期的響應(yīng)時間, long maxStaleMillis = 0; if (!responseCaching.mustRevalidate() && requestCaching.maxStaleSeconds() != -1) { maxStaleMillis = SECONDS.toMillis(requestCaching.maxStaleSeconds()); } //規(guī)則,緩存的年齡加上期望指定的有效時間期限小于實際的保鮮值加上過期時間 if (!responseCaching.noCache() && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) { //雖然緩存能使用但是已經(jīng)過期了這時候要在header內(nèi)加提醒 Response.Builder builder = cacheResponse.newBuilder(); if (ageMillis + minFreshMillis >= freshMillis) { builder.addHeader("Warning", "110 HttpURLConnection "Response is stale""); } //緩存已經(jīng)超過一天了雖然還沒有過期加提醒 long oneDayMillis = 24 * 60 * 60 * 1000L; if (ageMillis > oneDayMillis && isFreshnessLifetimeHeuristic()) { builder.addHeader("Warning", "113 HttpURLConnection "Heuristic expiration""); } return new CacheStrategy(null, builder.build()); }

1.2.2、計算緩存從產(chǎn)生開始到現(xiàn)在的年齡(RFC 7234)

確定緩存的年齡的參數(shù)是一下四個。

發(fā)起請求時間:sentRequestMillis。

接收響應(yīng)時間:receivedResponseMillis。

當(dāng)前時間:nowMillis。

響應(yīng)頭中的age時間:ageSeconds,文件在緩存服務(wù)器中存在的時間。

根據(jù)RFC 7234計算的算法如下。

private long cacheResponseAge() { //接收到的時間減去資源在服務(wù)器端產(chǎn)生的時間得到apparentReceivedAge long apparentReceivedAge = servedDate != null ");

1.2.3、計算CacheRespone中的保鮮期

如果在CacheResone的cacheContro中獲取maxAgeSecends就是保鮮器

否則,就嘗試在expires header中獲取值減去服務(wù)器響應(yīng)的時間就是保鮮期

否在,服務(wù)器時間減去lastmodifide的時間的十分之一做為保鮮期。

private long computeFreshnessLifetime() { //cacheRespone中的control CacheControl responseCaching = cacheResponse.cacheControl(); if (responseCaching.maxAgeSeconds() != -1) { //如果cacheContro中存在maxAgeSecends直接使用 return SECONDS.toMillis(responseCaching.maxAgeSeconds()); } else if (expires != null) { //如果沒有max-age就使用過期時間減去服務(wù)器產(chǎn)生時間 long servedMillis = servedDate != null ");

2、對響應(yīng)CacheStrategy的處理

當(dāng)networkRequest為空且cahceResponse為空的時候,表示可以使用緩存且現(xiàn)在的緩存不可用,返回504。responecode 504的語義:但是沒有及時從上游服務(wù)器收到請求。

當(dāng)networRequest為空且cacheResponse不為空表示,可以使用緩存且緩存可用,就可以直接返回緩存response了。

當(dāng)networkRequest為空的時候,表示需要和服務(wù)器進(jìn)行校驗,或者直接去請求服務(wù)器。

響應(yīng)碼為304表示經(jīng)服務(wù)器效驗緩存的響應(yīng)式有效的可以使用,更新緩存年齡。

響應(yīng)碼不為304更新緩存,返回響應(yīng);且有可能響應(yīng)式不可用的,返回body為空,header有信息的respone。

@Override public Response intercept(Chain chain) throws IOException { Response cacheCandidate = cache != null ");

3、解惑

3.1、 ETAG和if-Modified-Sine什么時候生效

在CacheSategry里從respone中獲取的,如果存在Etag或者M(jìn)odify的字段就只用在ConditionalRequet設(shè)置對應(yīng)的值做請求了,帶有條件的請求,去服務(wù)器驗證。

3.2、Request中的no-cache的語義以及和no-store的區(qū)別

nocache的意思是不使用不可靠的緩存響應(yīng),必須經(jīng)過服務(wù)器驗證的才能使用

CacheStrategy#Factory#getCandidate()中

CacheControl requestCaching = request.cacheControl(); if (requestCaching.noCache() || hasConditions(request)) { return new CacheStrategy(request, null); }

在CacheStrategy中的判斷邏輯是:當(dāng)request中請求頭中cache-control的值為no-cache的時候或者請求頭中存在if-none-match或者if-modified-sine的時候直接去請求服務(wù)放棄緩存。

3.3、 什么條件下會使用緩存呢

當(dāng)緩存可用,沒有超過有效期,且不需要經(jīng)過驗證的時候可以直接從緩存中獲取出來。

需要經(jīng)過驗證,經(jīng)過服務(wù)端驗證,響應(yīng)碼為304表示緩存有效可以使用。

3.4、 must-revalidate 、no-cache、max-age = 0區(qū)別

must-revalidate(響應(yīng)中cacheContorl的值)
如果過期了就必須去服務(wù)器做驗證不能夠使用過期的資源,這標(biāo)志了max-StaleSe失效

no-cache 只能使用源服務(wù)效驗過的respone,不能使用未經(jīng)效驗的respone。

根據(jù)Okhttp代碼中處理的策略是在request中cache-control為no-cache的時候,就直接去服務(wù)端去請求,如果需要添加額外的條件需要自己手動去添加。

在respone中cache-control的條件為no-cache的時候表示客戶端使用cache的時候需要經(jīng)過服務(wù)器的驗證,使用If-None-Match或者If-Modified-Since。

max-age=0表示保質(zhì)期為0,表示過了保鮮期,也就是需要去驗證了。

4、小結(jié)

通過本篇我們知道了http協(xié)議的緩存策略,已經(jīng)在okhttp中是如何實踐的。總的來說會有這樣幾個步驟

判斷是否符合使用緩存的條件,是否有響應(yīng)緩存,根據(jù)cache-contorl字段緩存是否可用,是否過期。

如果需要服務(wù)器端進(jìn)行驗證,主要是兩種方式request的header中 if-none-match:etag和If-Modified-Since:時間戳。

響應(yīng)碼304表示驗證通過,非304表示緩存不可用更新本地緩存。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/6709.html

相關(guān)文章

  • 前端開發(fā)中常見的 HTTP 相關(guān)知識(一)

    摘要:前言前端開發(fā)中總是要和接口和緩存打交道,所以相關(guān)內(nèi)容多多少少還是要知道一些,干起活來才能事半功倍。處于中的應(yīng)用層。部分安全性問題發(fā)布于年的版本,也是當(dāng)前的最新標(biāo)準(zhǔn)。基于谷歌提出的而來,之前用于瀏覽器中來訪問的加密服務(wù),在發(fā)布后功成身退。 前言 前端開發(fā)中總是要和接口和緩存打交道,所以HTTP相關(guān)內(nèi)容多多少少還是要知道一些,干起活來才能事半功倍。下面我從業(yè)務(wù)出發(fā),簡單說下一些可能會碰到的...

    劉東 評論0 收藏0
  • 重學(xué)前端學(xué)習(xí)筆記(十一)--瀏覽器工作解析(一)

    摘要:緊跟在后面的是請求頭,每行用冒號分隔名稱和值按下兩次回車,收到服務(wù)端回復(fù)響應(yīng)部分第一行被稱作,它也分為三個部分,協(xié)議和版本狀態(tài)碼和狀態(tài)文本。對前端來說系列的狀態(tài)碼是非常陌生的,原因是的狀態(tài)被瀏覽器庫直接處理掉了,不會讓上層應(yīng)用知曉。 筆記說明 重學(xué)前端是程劭非(winter)【前手機(jī)淘寶前端負(fù)責(zé)人】在極客時間開的一個專欄,每天10分鐘,重構(gòu)你的前端知識體系,筆者主要整理學(xué)習(xí)過程的一些要...

    paulli3 評論0 收藏0
  • 重學(xué)前端學(xué)習(xí)筆記(十一)--瀏覽器工作解析(一)

    摘要:緊跟在后面的是請求頭,每行用冒號分隔名稱和值按下兩次回車,收到服務(wù)端回復(fù)響應(yīng)部分第一行被稱作,它也分為三個部分,協(xié)議和版本狀態(tài)碼和狀態(tài)文本。對前端來說系列的狀態(tài)碼是非常陌生的,原因是的狀態(tài)被瀏覽器庫直接處理掉了,不會讓上層應(yīng)用知曉。 筆記說明 重學(xué)前端是程劭非(winter)【前手機(jī)淘寶前端負(fù)責(zé)人】在極客時間開的一個專欄,每天10分鐘,重構(gòu)你的前端知識體系,筆者主要整理學(xué)習(xí)過程的一些要...

    付永剛 評論0 收藏0
  • 重學(xué)前端學(xué)習(xí)筆記(十一)--瀏覽器工作解析(一)

    摘要:緊跟在后面的是請求頭,每行用冒號分隔名稱和值按下兩次回車,收到服務(wù)端回復(fù)響應(yīng)部分第一行被稱作,它也分為三個部分,協(xié)議和版本狀態(tài)碼和狀態(tài)文本。對前端來說系列的狀態(tài)碼是非常陌生的,原因是的狀態(tài)被瀏覽器庫直接處理掉了,不會讓上層應(yīng)用知曉。 筆記說明 重學(xué)前端是程劭非(winter)【前手機(jī)淘寶前端負(fù)責(zé)人】在極客時間開的一個專欄,每天10分鐘,重構(gòu)你的前端知識體系,筆者主要整理學(xué)習(xí)過程的一些要...

    alighters 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<