1. 程式人生 > >OKHTTP緩存max-age和max-stale詳解

OKHTTP緩存max-age和max-stale詳解

expire const 8.0 ogg ML syn orm -c serve

一、前言 此前了解過OKHTTP的緩存的使用,並對緩存的使用進行了初步的總結https://blog.csdn.net/polo2044/article/details/80650856。緩存主要是為了在沒有網絡的情況下,或者資源不需要實時去後端進行更新時,可以直接從緩存中獲取資源信息。一方面是能夠在斷網的情況下仍然給用戶良好的界面展示,另一個方面是避免頻繁的請求網絡給後端帶來壓力。目前網絡上介紹的在OKHTTP中實現客戶端的緩存機制,都是在自定義的Intercept中設置max-age和max-stale的參數值來完成。 但是在查閱網絡上的方法後,依舊存在如下疑問: 1. max-age和max-stale在request和response中都可以進行設置,兩種設置的效果是否一樣。 2.在本人公司的URL測試中發現,max-age和max-stale沒有起作用。 為了解決這些疑問,因此有必要在實際代碼中對這兩個參數進行實際的驗證和研究。 二、max-age和max-stale參數的意義
max-age:告知緩存多長時間,在沒有超過緩存時間的情況下,請求會返回緩存內的數據,在超出max-age的情況下向服務端發起新的請求,請求失敗的情況下返回緩存數據(測試中已驗證),否則向服務端重新發起請求。 max-stale:指示客戶機可以接收超出max-age時間的響應消息,max-stale在請求設置中有效,在響應設置中無效(測試中已驗證,且參見博客https://www.jianshu.com/p/db197279f053)。 因此max-age和max-stale在請求中同時使用的情況下,緩存的時間可以為max-age和max-stale的和。 三、測試驗證
在測試中需要構造一個OKHTTP,然後在攔截器中定義不同的max-age和max-stale來進行測試,在Activity中定義一個定時器,每隔3s進行一次網絡請求。 Activity中的網絡請求如下:
@Override public void onClick(View view) { if (view.getId() == R.id.button) { Observable.interval(3, TimeUnit.SECONDS).subscribe( new Action1<Long>() { @Override public void call(Long aLong) { getRecomandComic(); } } ); } } private void getRecomandComic() { RetrofitResearch.getInstance(this) .getTopRecommendList() .subscribeOn(Schedulers.computation()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Action1<ComicBeanNoCountResult>() { @Override public void call(ComicBeanNoCountResult comicBeanNoCountResult) { } }, new Action1<Throwable>() { @Override public void call(Throwable throwable) { } }); }
OKHTTP的構造代碼如下:
public class OkHttpCallFactory { private static int DEFAULT_TIMEOUT = 30; private static final int CONST_10 = 10; private static final int CONST_1024 = 1024; private static final Logger logger = Logger.getLogger("yansm"); private OkHttpCallFactory() { } private static Call.Factory factory; private static int time =60; public static Call.Factory getOkHttpFactory(Context context) { if (factory == null) { synchronized (OkHttpCallFactory.class) { if (factory == null) { OkHttpClient.Builder builder = new OkHttpClient.Builder(); builder.cache(new Cache(context.getCacheDir(), CONST_10 * CONST_1024 * CONST_1024)); //設置緩存 builder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS); builder.writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS); builder.readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS); Interceptor interceptor = new NetCacheInterceptor(); builder.addInterceptor(interceptor); builder.addNetworkInterceptor(interceptor); builder.addInterceptor(new LoggingInterceptor()); factory = builder.build(); } } } return factory; } //日誌輸出的攔截器 private static class LoggingInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { long t1 = System.nanoTime(); Request request = chain.request(); logger.info(String.format("Sending request %s on %s%n%s", request.url(), chain.connection(), request.headers())); Response response = chain.proceed(request); long t2 = System.nanoTime(); logger.info(String.format("Received response for %s in %.1fms%n%s", request.url(), (t2 - t1) / 1e6d, response.headers())); return response; } } }
3.1 在攔截器的response中設置max-age = 60和max-stale = 120
public class NetCacheInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); CacheControl cacheControl = new CacheControl.Builder() .maxAge(60, TimeUnit.SECONDS) .maxStale(120, TimeUnit.SECONDS).build(); Response response = chain.proceed(request); return response.newBuilder() .removeHeader("Pragma") .removeHeader("Cache-Control") .header("Cache-Control", cacheControl.toString()) .build(); } }
測試日誌結果如下,每隔三秒發送一次請求:
第一次網絡請求 06-22 13:59:51.816 32336-469/com.example.moxie.cacheresearch I/yansm: Sending request http://lbs.sougu.net.cn/app.php?m=souguapp&c=appusers&a=network on null 06-22 13:59:52.200 32336-469/com.example.moxie.cacheresearch I/yansm: Received response for http://lbs.sougu.net.cn/app.php?m=souguapp&c=appusers&a=network in 381.9ms Server: Tengine Content-Type: text/json;charset=UTF-8 Content-Length: 1492 Date: Fri, 22 Jun 2018 05:59:51 GMT Expires: Thu, 19 Nov 1981 08:52:00 GMT X-Powered-By: PHP/7.0.1 Set-Cookie: PHPSESSID=pef8qomvm3kjg90bs94ojpugf1; path=/ Set-Cookie: SERVERID=37f80876073565188d4968aa47392891|1529647191|1529647191;Path=/ Via: cache14.l2eu6-1[103,200-0,M], cache4.l2eu6-1[103,0], cache8.cn785[155,200-0,M], cache7.cn785[159,0] X-Cache: MISS TCP_MISS dirn:-2:-2 mlen:-1 X-Swift-SaveTime: Fri, 22 Jun 2018 05:59:51 GMT X-Swift-CacheTime: 0 Timing-Allow-Origin: * EagleId: 3d9bde1b15296471912141111e Connection: keep-alive Cache-Control: max-age=60, max-stale=120 第二次網絡請求 06-22 13:59:54.733 32336-469/com.example.moxie.cacheresearch I/yansm: Sending request http://lbs.sougu.net.cn/app.php?m=souguapp&c=appusers&a=network on null 06-22 13:59:54.759 32336-469/com.example.moxie.cacheresearch I/yansm: Received response for http://lbs.sougu.net.cn/app.php?m=souguapp&c=appusers&a=network in 25.1ms Server: Tengine Content-Type: text/json;charset=UTF-8 Content-Length: 1492 Date: Fri, 22 Jun 2018 05:59:51 GMT Expires: Thu, 19 Nov 1981 08:52:00 GMT X-Powered-By: PHP/7.0.1 Set-Cookie: PHPSESSID=pef8qomvm3kjg90bs94ojpugf1; path=/ Set-Cookie: SERVERID=37f80876073565188d4968aa47392891|1529647191|1529647191;Path=/ Via: cache14.l2eu6-1[103,200-0,M], cache4.l2eu6-1[103,0], cache8.cn785[155,200-0,M], cache7.cn785[159,0] X-Cache: MISS TCP_MISS dirn:-2:-2 mlen:-1 X-Swift-SaveTime: Fri, 22 Jun 2018 05:59:51 GMT X-Swift-CacheTime: 0 Timing-Allow-Origin: * EagleId: 3d9bde1b15296471912141111e Connection: keep-alive Cache-Control: max-age=60, max-stale=120 第三次網絡請求 06-22 13:59:57.733 32336-469/com.example.moxie.cacheresearch I/yansm: Sending request http://lbs.sougu.net.cn/app.php?m=souguapp&c=appusers&a=network on null 06-22 13:59:57.749 32336-469/com.example.moxie.cacheresearch I/yansm: Received response for http://lbs.sougu.net.cn/app.php?m=souguapp&c=appusers&a=network in 14.9ms Server: Tengine Content-Type: text/json;charset=UTF-8 Content-Length: 1492 Date: Fri, 22 Jun 2018 05:59:51 GMT Expires: Thu, 19 Nov 1981 08:52:00 GMT X-Powered-By: PHP/7.0.1 Set-Cookie: PHPSESSID=pef8qomvm3kjg90bs94ojpugf1; path=/ Set-Cookie: SERVERID=37f80876073565188d4968aa47392891|1529647191|1529647191;Path=/ Via: cache14.l2eu6-1[103,200-0,M], cache4.l2eu6-1[103,0], cache8.cn785[155,200-0,M], cache7.cn785[159,0] X-Cache: MISS TCP_MISS dirn:-2:-2 mlen:-1 X-Swift-SaveTime: Fri, 22 Jun 2018 05:59:51 GMT X-Swift-CacheTime: 0 Timing-Allow-Origin: * EagleId: 3d9bde1b15296471912141111e Connection: keep-alive Cache-Control: max-age=60, max-stale=120
charles抓包結果 技術分享圖片 技術分享圖片 測試結果分析:在實驗中,對response的max-age和max-stale進行了設置,分別為60秒和120秒,在日誌的輸出中可以看到這一點。但是通過實際的抓包結果能夠看到max-age時效起作用了,max-stale沒有起時效,客戶端在60s內沒有向服務端發送請求,但是在60s後向服務端發送了請求。 3.2 在攔截器的request中設置max-age=120和max-stale=60 參考代碼如下
@Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); CacheControl cacheControl = new CacheControl.Builder().maxAge(120, TimeUnit.SECONDS) .maxStale(60, TimeUnit.SECONDS).build(); request = request.newBuilder() .cacheControl(cacheControl) .build(); return chain.proceed(request); }
測試日誌結果如下:
第一次網絡請求 06-22 13:33:28.131 29674-30389/com.example.moxie.cacheresearch I/yansm: Sending request http://lbs.sougu.net.cn/app.php?m=souguapp&c=appusers&a=network on null Cache-Control: max-age=120, max-stale=60 06-22 13:33:28.700 29674-30389/com.example.moxie.cacheresearch I/yansm: Received response for http://lbs.sougu.net.cn/app.php?m=souguapp&c=appusers&a=network in 567.5ms Server: Tengine Content-Type: text/json;charset=UTF-8 Content-Length: 1492 Date: Fri, 22 Jun 2018 05:33:27 GMT Expires: Thu, 19 Nov 1981 08:52:00 GMT X-Powered-By: PHP/7.0.1 Set-Cookie: PHPSESSID=t6j4tnss1nkpbm0tddqdr0o2j1; path=/ Set-Cookie: SERVERID=37f80876073565188d4968aa47392891|1529645607|1529645607;Path=/ Via: cache14.l2eu6-1[102,200-0,M], cache21.l2eu6-1[106,0], cache10.cn883[190,200-0,M], cache1.cn883[214,0] X-Cache: MISS TCP_MISS dirn:-2:-2 mlen:-1 X-Swift-SaveTime: Fri, 22 Jun 2018 05:33:27 GMT X-Swift-CacheTime: 0 Timing-Allow-Origin: * EagleId: af06f99515296456075954226e Connection: keep-alive Cache-Control: public, max-age=6000 第N次網絡請求 06-22 13:35:25.057 29674-30389/com.example.moxie.cacheresearch I/yansm: Received response for http://lbs.sougu.net.cn/app.php?m=souguapp&c=appusers&a=network in 9.6ms Server: Tengine Content-Type: text/json;charset=UTF-8 Content-Length: 1492 Date: Fri, 22 Jun 2018 05:33:27 GMT Expires: Thu, 19 Nov 1981 08:52:00 GMT X-Powered-By: PHP/7.0.1 Set-Cookie: PHPSESSID=t6j4tnss1nkpbm0tddqdr0o2j1; path=/ Set-Cookie: SERVERID=37f80876073565188d4968aa47392891|1529645607|1529645607;Path=/ Via: cache14.l2eu6-1[102,200-0,M], cache21.l2eu6-1[106,0], cache10.cn883[190,200-0,M], cache1.cn883[214,0] X-Cache: MISS TCP_MISS dirn:-2:-2 mlen:-1 X-Swift-SaveTime: Fri, 22 Jun 2018 05:33:27 GMT X-Swift-CacheTime: 0 Timing-Allow-Origin: * EagleId: af06f99515296456075954226e Connection: keep-alive Cache-Control: public, max-age=6000 第N+1次網絡請求 06-22 13:35:28.046 29674-30389/com.example.moxie.cacheresearch I/yansm: Sending request http://lbs.sougu.net.cn/app.php?m=souguapp&c=appusers&a=network on null Cache-Control: max-age=120, max-stale=60 06-22 13:35:28.056 29674-30389/com.example.moxie.cacheresearch I/yansm: Received response for http://lbs.sougu.net.cn/app.php?m=souguapp&c=appusers&a=network in 8.9ms Server: Tengine Content-Type: text/json;charset=UTF-8 Content-Length: 1492 Date: Fri, 22 Jun 2018 05:33:27 GMT Expires: Thu, 19 Nov 1981 08:52:00 GMT X-Powered-By: PHP/7.0.1 Set-Cookie: PHPSESSID=t6j4tnss1nkpbm0tddqdr0o2j1; path=/ Set-Cookie: SERVERID=37f80876073565188d4968aa47392891|1529645607|1529645607;Path=/ Via: cache14.l2eu6-1[102,200-0,M], cache21.l2eu6-1[106,0], cache10.cn883[190,200-0,M], cache1.cn883[214,0] X-Cache: MISS TCP_MISS dirn:-2:-2 mlen:-1 X-Swift-SaveTime: Fri, 22 Jun 2018 05:33:27 GMT X-Swift-CacheTime: 0 Timing-Allow-Origin: * EagleId: af06f99515296456075954226e Connection: keep-alive Cache-Control: public, max-age=6000 Warning: 110 HttpURLConnection "Response is stale"
charles抓包結果 技術分享圖片 技術分享圖片 測試結果分析,從測試結果中可以看出在request請求中設置max-age和max-stale值,緩存時間是以兩者值得和,測試中max-age = 120s,max-stale = 60s,恰好為3分鐘,抓包請求間隔是3分鐘。但是有一點,在請求的時間超過120s後,再次請求時,請求的結果中會多出“Warning: 110 HttpURLConnection "Response is stale"” 3.3 異常測試一 上述測試中max-age和max-stale還算符合預期,但是在用公司的url進行測試時,max-age和max-stale似乎失去了效果。依舊在response中設置max-age =60和max-stale =120進行測試。 測試日誌結果如下:
第一次網絡請求 06-22 17:38:27.094 7756-7780/com.example.moxie.cacheresearch I/yansm: Sending request https://comic.mkzhan.com/recom/app/ on null 06-22 17:38:27.253 7756-7780/com.example.moxie.cacheresearch I/yansm: Received response for https://comic.mkzhan.com/recom/app/ in 158.0ms Server: Tengine Content-Type: application/json; charset=utf-8 Content-Length: 625 Date: Fri, 22 Jun 2018 08:51:34 GMT Access-Control-Allow-Origin: * Access-Control-Allow-Headers: X-Requested-With,app-id,app-version,device-name,device-resolution,device-udid,platform,system-name,system-version Access-Control-Allow-Methods: GET,POST,OPTIONS X-Powered-By: mkzhan-A2 Via: cache9.l2cm10-1[0,200-0,H], cache29.l2cm10-1[1,0], cache1.cn1412[0,200-0,H], cache14.cn1412[1,0] Age: 2812 X-Cache: HIT TCP_MEM_HIT dirn:12:43491335 mlen:-1 X-Swift-SaveTime: Fri, 22 Jun 2018 09:03:10 GMT X-Swift-CacheTime: 3600 Timing-Allow-Origin: * EagleId: 7ce89ea215296603064393222e Connection: keep-alive Cache-Control: max-age=60, max-stale=120 第二次網絡請求 06-22 17:38:30.091 7756-7780/com.example.moxie.cacheresearch I/yansm: Sending request https://comic.mkzhan.com/recom/app/ on null 06-22 17:38:30.249 7756-7780/com.example.moxie.cacheresearch I/yansm: Received response for https://comic.mkzhan.com/recom/app/ in 156.9ms Server: Tengine Content-Type: application/json; charset=utf-8 Content-Length: 625 Date: Fri, 22 Jun 2018 08:51:34 GMT Access-Control-Allow-Origin: * Access-Control-Allow-Headers: X-Requested-With,app-id,app-version,device-name,device-resolution,device-udid,platform,system-name,system-version Access-Control-Allow-Methods: GET,POST,OPTIONS X-Powered-By: mkzhan-A2 Via: cache9.l2cm10-1[0,200-0,H], cache29.l2cm10-1[1,0], cache1.cn1412[0,200-0,H], cache14.cn1412[1,0] Age: 2815 X-Cache: HIT TCP_MEM_HIT dirn:12:43491335 mlen:-1 X-Swift-SaveTime: Fri, 22 Jun 2018 09:03:10 GMT X-Swift-CacheTime: 3600 Timing-Allow-Origin: * EagleId: 7ce89ea215296603094162696e Connection: keep-alive Cache-Control: max-age=60, max-stale=120 第三次網絡請求 06-22 17:38:33.093 7756-7780/com.example.moxie.cacheresearch I/yansm: Sending request https://comic.mkzhan.com/recom/app/ on null 06-22 17:38:33.198 7756-7780/com.example.moxie.cacheresearch I/yansm: Received response for https://comic.mkzhan.com/recom/app/ in 103.4ms Server: Tengine Content-Type: application/json; charset=utf-8 Content-Length: 625 Date: Fri, 22 Jun 2018 08:51:34 GMT Access-Control-Allow-Origin: * Access-Control-Allow-Headers: X-Requested-With,app-id,app-version,device-name,device-resolution,device-udid,platform,system-name,system-version Access-Control-Allow-Methods: GET,POST,OPTIONS X-Powered-By: mkzhan-A2 Via: cache9.l2cm10-1[0,200-0,H], cache29.l2cm10-1[1,0], cache1.cn1412[0,200-0,H], cache14.cn1412[0,0] Age: 2818 X-Cache: HIT TCP_MEM_HIT dirn:12:43491335 mlen:-1 X-Swift-SaveTime: Fri, 22 Jun 2018 09:03:10 GMT X-Swift-CacheTime: 3600 Timing-Allow-Origin: * EagleId: 7ce89ea215296603123821715e Connection: keep-alive Cache-Control: max-age=60, max-stale=120
抓包結果如下 技術分享圖片 技術分享圖片 問題提出:在response中設置了max-age =60s,根據此前的測試情況,抓包得到的請求應該間隔60s,而不是根據請求每次都是3s。針對這一疑問,檢查了日誌中的返回字段,發現公司服務器返回的字段比此前測試的字段多了一個Age字段 技術分享圖片 技術分享圖片 Age字段的定義:當代理服務器用自己緩存的實體去響應請求時,用該頭部表明該實體從產生到現在經過多長時間了(可參見該博客的解釋https://blog.csdn.net/xifeijian/article/details/46460631)。 由該字段的定義可以猜到,該字段會影響緩存的時效,如果定義的緩存的比改值要小,則緩存從設置起可能就已經失效了。因此需要定義max-age的時間值超過Age的時間值才能讓max-age值生效。 3.4 異常測試二 由於Age的時間值也會會是增加的,將max-age的時間值設置為4000,得到如下測試日誌:
06-22 09:52:47.578 14683-14855/com.example.moxie.cacheresearch I/yansm: Sending request https://comic.mkzhan.com/recom/app/ on null 06-22 09:52:48.216 14683-14855/com.example.moxie.cacheresearch I/yansm: Received response for https://comic.mkzhan.com/recom/app/ in 637.9ms Server: Tengine Content-Type: application/json; charset=utf-8 Content-Length: 624 Date: Fri, 22 Jun 2018 00:51:28 GMT Access-Control-Allow-Origin: * Access-Control-Allow-Headers: X-Requested-With,app-id,app-version,device-name,device-resolution,device-udid,platform,system-name,system-version Access-Control-Allow-Methods: GET,POST,OPTIONS X-Powered-By: mkzhan-A2 Via: cache9.l2cm10-1[0,200-0,H], cache22.l2cm10-1[1,0], cache10.cn978[0,200-0,H], cache2.cn978[1,0] Age: 3679 X-Cache: HIT TCP_MEM_HIT dirn:6:301393262 mlen:-1 X-Swift-SaveTime: Fri, 22 Jun 2018 01:08:57 GMT X-Swift-CacheTime: 3600 Timing-Allow-Origin: * EagleId: 74d3999615296323674053464e Connection: keep-alive Cache-Control: public, max-age=4000
測試結果是,緩存生效了,但是生效的時間值只有4000s-3679s = 321s,在Age值超過4000s之後,依舊是按照測試代碼中設定的間隔每隔3s鐘發送一次。為了能夠除去Age造成的影響,在response中調用removeHeader將Age給刪除,但是並沒有起作用。 四、總結 本次主要是對max-age和max-stale的意義在OKHTTP的使用中進行了實際的驗證測試,由於起初用公司的URL進行測試時發現與預期不符,經過排查發現是Age參數值搞的鬼。 參考博客: https://blog.csdn.net/adzcsx2/article/details/51365548 https://blog.csdn.net/qqyanjiang/article/details/51316116 https://segmentfault.com/q/1010000002578217/a-1020000002578576 https://www.cnblogs.com/_franky/archive/2011/11/23/2260109.html https://blog.csdn.net/briblue/article/details/52920531 https://www.cnblogs.com/lenve/p/6063851.html https://blog.csdn.net/yangjianbo456/article/details/52975183 https://www.jianshu.com/p/db197279f053(比較詳細,值得細細閱讀)

OKHTTP緩存max-age和max-stale詳解