URLCache 设置不当影起的 App 故障

关于数据请求是几乎每个 App 会经常遇到的事,关于数据的 Request 与 Response 及 Cache 其实我们需要了解更多细节才能运用自如,排查问题更快。

很久之前,我时常遇到一部分用户能打开页面,一部用户却不能的情况。在排除网络,地域,机型,服务器状态完全正常的情况,一直无法解释其原因。客户也说不上来有什么区别,操作路径也是一样,只是过一段时间就自然恢复了,或重装等等解决。

直到最近的一次故障让我彻底将此类问题的原头分析了一遍,才有所顿悟。

故障

现象 服务端因某种原因使所有 API 请求返回错误的,类似源代码的片段内容。几个小时后服务器恢复。不过部分用户使用时看到的仍然是出错页面,而其他用户又是正常。

影响范围 后来仔细分析发现,在服务器宕机的那段时间内,用户如果打开过 App ,则一直会看到出错页面。即使完全退出 App 再进入错误仍旧发生。如果这段时间内没有打开过 App 的用户,在之后打开的话,就会看到正常的页面。所以影响面是所有服务器出错这段时间内,打开过 App 的用户。

协议缓存 NSURLRequestUseProtocolCachePolicy我们引用了官方给出的一张流程图来详细解释这个过程从上至下我们可以看一下整个过程

  1. 如果缓存内容不存在,不用说,直接发起原始请求了

  2. 如果缓存内容存在,确定是否每次从原始资源地址检查。这依赖于 Response Header 里 cach-control 指定的内容

    常见的值有 private、public、no-store、no-cache、must-revalidate、max-age等。

    no-cache: 告诉浏览器、缓存服务器,不管本地副本是否过期,使用资源副本前,一定要到源服务器进行副本有效性校验。

    must-revalidate:告诉浏览器、缓存服务器,本地副本过期前,可以使用本地副本;本地副本一旦过期,必须去源服务器进行有效性校验。

本例中 Response Header 没有指定 must-revalidate,所以条件是否。

Response Header
  1. 如果结果为是,则发起 HEAD request 请求,查看内容是否有变更,"是"则请求网络数据,如果否就用缓存

  2. 如果上面的结果是否,则查看 Response 是否失效 (stale?)

这里的失效算法算是黑盒了,不过还好发现篇文章,描述了如何计算出这个失效期限。

// 首先是计算 freshness_lifetime

freshness_lifetime = (date_value - last_modified) * 10% =>

// 例如: ([Sat, 09 Apr 2016 10:26:51 GMT] - [Sun, 17 Jan 2016 14:15:33 GMT]) 10% => [82 days, 19 hours, 11 minutes and 18 seconds] 10% => 7,153,878 * 10% = 715388 seconds = 8 days 6 hours 43 minutes 8 seconds

// 然后判断 response 是否失效是根据 freshness lifetime 是否大于 current age response_isfresh = (freshness_lifetime > current_age)

// 这里的 current age 是根据当前时间 now 减去 Response Header 里的 date_value 来的 current_age = now - date_value</pre> 5. 如果计算的结果是失效的 stale 是 true ,则去发送 issue request 请求。如果是否返回缓存数据</li> </ol> </li> 解决

避免缓存无效的 Response首先服务端对于错误的返回结果不能返回 200 status code 客户端要做强校验,对于返回的不规范的格式或非业务所需,直接 removeCachedResponse

设置强制清理缓存机制可以由客户手动清理缓存,或由服务端控制清理

区别对待不同的 API 请求 cache 策略客户端与服务端对不同的请求设定不同的缓存策略,比如配置下发那些是强制刷新不缓存的。不经常变的资源可以设置更长的缓存期限

参考资料

NSURLCache

iOS5 Built-In HTTP DiskCache

A Primer In Http–caching And Its–native Support–by–ios

NSURLCache Uses a Disk Cache as of iOS 5

工具

iOS SDK 原生抓包工具