> For the complete documentation index, see [llms.txt](https://www.doruby.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://www.doruby.com/mobile/debug/urlcache-she-zhi-bu-dang-ying-qi-de-app-gu-zhang.md).

# URLCache 设置不当引起的 App 故障

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

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

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

&#x20;故障

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

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

&#x20;   协议缓存 NSURLRequestUseProtocolCachePolicy我们引用了官方给出的一张流程图来详细解释这个过程![](https://docs-assets.developer.apple.com/published/d24b133ea3/http_caching_2x_820a949f-9d5d-4a85-9ca2-50b42b339e18.png)从上至下我们可以看一下整个过程

1. 如果缓存内容不存在，不用说，直接发起原始请求了
2. 如果缓存内容存在，确定是否每次从原始资源地址检查。这依赖于 Response Header 里 [cach-control](https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html) 指定的内容

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

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

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

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

![Response Header](http://www.doruby.com/wp-content/uploads/2017/03/Screen-Shot-2017-03-06-at-7.34.11-PM.png)

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](http://nshipster.cn/nsurlcache/)

[iOS5 Built-In HTTP DiskCache](https://gist.github.com/bogardon/3245415)

[A Primer In Http–caching And Its–native Support–by–ios](http://qnoid.com/2016/04/10/A-primer-in-HTTP-caching-and-its-native-support-by-iOS.html)

[NSURLCache Uses a Disk Cache as of iOS 5](http://petersteinberger.com/blog/2012/nsurlcache-uses-a-disk-cache-as-of-ios5/)

工具

[iOS SDK 原生抓包工具](http://www.appscaffold.net)
