zoukankan      html  css  js  c++  java
  • OKHTTP缓存max-age和max-stale详解

    一、前言
          此前了解过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://www.jianshu.com/p/db197279f053(比较详细,值得细细阅读)
     

  • 相关阅读:
    信创
    Tuxedo 学习记录
    独立软件开发商
    将博客搬至CSDN
    《Java从入门到放弃》JavaSE入门篇:网络编程(入门版)
    《Java从入门到放弃》JavaSE入门篇:练习——单身狗租赁系统
    《Java从入门到放弃》JavaSE入门篇:JDBC(入门版)
    《Java从入门到放弃》JavaSE入门篇:文件操作
    《Java从入门到放弃》JavaSE入门篇:单元测试
    《Java从入门到放弃》JavaSE入门篇:异常
  • 原文地址:https://www.cnblogs.com/polo2044/p/9217043.html
Copyright © 2011-2022 走看看