当前位置: 首页 > news >正文

HTTP中的ETag在移动客户端的应用

绝大多数移动客户端在设计网络模块时,都会选用HTTP作为客户端和服务端通信的网络协议。随着业务的不断发展以及用户量的持续增长,整个客户端的稳定性和性能会逐渐成为关注的焦点,其中网络的性能优化更是重中之重,本文介绍的 ETag 缓存技术,可以在缓存数据的同时做到数据的实时更新,适用于对数据实效性要求较高的业务。

基本原理和概念

相同的两次请求返回的结果相同时,第一次返回的结果缓存在客户端,第二次服务端不再返回结果,仅返回一个特殊的状态码,告诉客户端第二次请求的结果与上次相同,可以直接使用上次返回的数据。

实现中,会用到HTTP头中的两个字段:

  • ETag 返回应答数据的标记,服务端生成发送给客户端

  • If-None-Match 同样的请求,上一次返回的 ETag 值

交互过程

  • 服务端在将数据发送给客户端之前,首先计算应答数据的摘要(通常是MD5),把计算结果作为 ETag 的值,和数据一同发送给客户端。

  • 客户端收到应答数据后,检测 HTTP Header 中是否有 ETag 字段,如有则缓存应答数据和 ETag 的值。

  • 客户端再次发起同一请求时,读取上次缓存的 ETag 值,将其作为 If-None-Match 的值,并与请求数据一同发送给服务端。

  • 服务端收到请求,执行请求,在把应答数据返回给客户端之前计算摘要,并与客户端上报的摘要比较,如果两次摘要相同,说明本次的应答数据与上一次请求的应答数据相同,且客户端已缓存该数据,则简单返回304。

  • 客户端收到304,直接读取本地缓存的数据返回给调用网络模块的业务方。

交互过程总结如下图:

ETag

代码实现

本文的示例代码使用 NSURLSession 实现,由于 NSURLSession 完善的缓存策略,为了演示 ETag 的用法,需要先关闭缓存。

let config = NSURLSessionConfiguration.defaultSessionConfiguration()
config.requestCachePolicy = NSURLRequestCachePolicy.ReloadIgnoringCacheData
let session = NSURLSession(configuration: config)

NSURLSessionConfiguration 定义了 NSURLSession 在上传和下载时的行为及策略,我们指定了请求的缓存策略为 ReloadIgnoringCacheData, 意思就是在请求时不使用本地缓存。

创建请求的 NSURLRequest 对象:

let url = NSURL(string: "http://www.joywek.com/50x.html")!
let request = NSMutableURLRequest(URL: url)

上面会下载指定静态页面的内容,这个页面是放在 Nginx 的服务器上,Nginx 默认会对应答数据计算 ETag。当然,在实际的应用中请求的都是动态数据,服务器要动态计算 ETag 的值。

设置 HTTP Header 中的 If-None-Match 字段:

if let tag = self.findTagByURL(url) {
    request.addValue(tag, forHTTPHeaderField: "If-None-Match")
}

在发起请求时,先检查相同的请求是否存在 ETag,如果存在就,就意味着上次请求的应答数据已缓存。

发起请求并处理应答数据:

self.dataTask = session.dataTaskWithRequest(request,
    completionHandler: { (var data, response, error) -> Void in
        data = self.handleResponse(response!, data!, request)
})
self.dataTask?.resume()

如果 HTTP 返回的状态码是 200,说明是服务器正常返回数据,此时记录 ETag 的值并缓存应答数据:

if (resp.statusCode == 200) {
    self.etags[response.URL!] = resp.allHeaderFields["ETag"] as? String
    let cachedResponse = NSCachedURLResponse(response: resp, data: data)
    NSURLCache.sharedURLCache().storeCachedResponse(cachedResponse, forRequest: request)
    return data
}

如果返回 304,说明应答数据没有变化,与上次请求的一样,则直接返回缓存中的数据:

else if (resp.statusCode == 304) {
    let cachedResponse = NSURLCache.sharedURLCache().cachedResponseForRequest(request)
    return cachedResponse?.data
}

关于演示 Demo

下载地址:http://www.joywek.com/res/ETagExample.zip

相关文章:

  • java中的包
  • 数据结构例程——选择排序之堆排序
  • java中的继承
  • ​批处理文件中的errorlevel用法
  • 上转型对象
  • 方法重写与成员变量隐藏
  • ScriptManager(脚本控制器)
  • espcms会员二次开发文件说明——会员,时间格式
  • 问题4_1(已解决)
  • final关键字
  • abstract关键字
  • java中的接口
  • Android---利用android-async-http开源项目返回json数据
  • 内嵌类
  • 匿名类
  • 【Leetcode】104. 二叉树的最大深度
  • Android系统模拟器绘制实现概述
  • Consul Config 使用Git做版本控制的实现
  • Java 实战开发之spring、logback配置及chrome开发神器(六)
  • js如何打印object对象
  • k个最大的数及变种小结
  • nodejs实现webservice问题总结
  • oldjun 检测网站的经验
  • React16时代,该用什么姿势写 React ?
  • Redux 中间件分析
  • ubuntu 下nginx安装 并支持https协议
  • 闭包--闭包之tab栏切换(四)
  • 技术:超级实用的电脑小技巧
  • 你不可错过的前端面试题(一)
  • 算法-插入排序
  • 在electron中实现跨域请求,无需更改服务器端设置
  • ​【已解决】npm install​卡主不动的情况
  • ###STL(标准模板库)
  • #经典论文 异质山坡的物理模型 2 有效导水率
  • #我与Java虚拟机的故事#连载07:我放弃了对JVM的进一步学习
  • (10)工业界推荐系统-小红书推荐场景及内部实践【排序模型的特征】
  • (SpringBoot)第七章:SpringBoot日志文件
  • (博弈 sg入门)kiki's game -- hdu -- 2147
  • (超详细)语音信号处理之特征提取
  • (附源码)ssm智慧社区管理系统 毕业设计 101635
  • (接口自动化)Python3操作MySQL数据库
  • (一)基于IDEA的JAVA基础12
  • (译) 函数式 JS #1:简介
  • (原)记一次CentOS7 磁盘空间大小异常的解决过程
  • .desktop 桌面快捷_Linux桌面环境那么多,这几款优秀的任你选
  • .NET CORE 第一节 创建基本的 asp.net core
  • .NET Core 项目指定SDK版本
  • .NET Framework 的 bug?try-catch-when 中如果 when 语句抛出异常,程序将彻底崩溃
  • .Net 垃圾回收机制原理(二)
  • .NET 自定义中间件 判断是否存在 AllowAnonymousAttribute 特性 来判断是否需要身份验证
  • .NET/MSBuild 中的发布路径在哪里呢?如何在扩展编译的时候修改发布路径中的文件呢?
  • .NET8.0 AOT 经验分享 FreeSql/FreeRedis/FreeScheduler 均已通过测试
  • .net连接MySQL的方法
  • .NET下ASPX编程的几个小问题
  • /usr/bin/env: node: No such file or directory