缓存策略

作者: 苏永茂 | 来源:发表于2016-07-05 17:47 被阅读1099次

    缓存策略

    我们做缓存的目的 :

    • 没有网络的时候,读取硬盘中的缓存数据 加载上次瞬间的数据
    • 在网络不好,下载速度慢的时候,避免空白页,先读取缓存,下载后更新数据 。
    更具需要,需要做的缓存就是磁盘缓存 。需要数据之前,判断网络 。读取磁盘中上一次打开app的缓存 。

    选取缓存技术

    针对的功能的单一 。使用 NSURLCache

    启用系统缓存

            NSURLCache *urlCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024 diskCapacity:20 * 1024 * 1024 diskPath:nil];
            [NSURLCache setSharedURLCache:urlCache];        
    

    需要的接口 :

    • 判断缓存文件是否存在 如果没有创建
    • 文件中是否有缓存内容 如果没有加载当前瞬间的数据到缓存文件
    • 读取缓存路径中文件的缓存
    • 清空缓存

    实现

    • 使用get请求
    • 设置内存缓存大小 、磁盘缓存大笑 、缓存路径 。

    判断缓存的时效性

    服务器的文件存贮,大多采用资源变动后就重新生成一个链接的做法。
    但同时也不排除不同文件使用同一个链接。那么如果服务端的file更改了,本地已经有了缓存。如何更新缓存?

    • Last-Modified
      顾名思义,是资源最后修改的时间戳,往往与缓存时间进行对比来判断缓存是否过期
    -  在浏览器第一次请求某一个URL时,服务器端的返回状态会是200,内容是你请求的资源,同时有一个Last-Modified的属性标记此文件在服务期端最后被修改的时间,格式类似这样:
    

    Last-Modified: Fri, 12 May 2006 18:53:33 GMT
    - 客户端第二次请求此URL时,根据 HTTP 协议的规定,浏览器会向服务器传送 If-Modified-Since 报头,询问该时间之后文件是否有被修改过:
    If-Modified-Since: Fri, 12 May 2006 18:53:33 GMT
    - 如果服务器端的资源没有变化,则自动返回 HTTP 304 (Not Changed.)状态码,内容为空,这样就节省了传输数据量。当服务器端代码发生改变或者重启服务器时,则重新发出资源,返回和第一次请求时类似。从而保证不向客户端重复发出资源,也保证当服务器有变化时,客户端能够得到最新的资源。

    • ETag

      ETag是一个可以与Web资源关联的记号(token)。它是一个 hash 值,用作 Request 缓存请求头,每一个资源文件都对应一个唯一的 ETag 值,服务器单独负责判断记号是什么及其含义,并在HTTP响应头中将其传送到客户端,以下是服务器端返回的格式:
      ETag: "50b1c1d4f775c61:df3"
      客户端的查询更新格式是这样的:
      If-None-Match: W/"50b1c1d4f775c61:df3"

    那么判断规则就是这样的 :

                if LastModifiedFromServer != LastModifiedOnClien
                 GetFromServer      
                else
                 GetFromCache
    
    /*!
     @brief 如果本地缓存资源为最新,则使用使用本地缓存。如果服务器已经更新或本地无缓存则从服务器请求资源。
     
     @details
     
     步骤:
     1. 请求是可变的,缓存策略要每次都从服务器加载
     2. 每次得到响应后,需要记录住 LastModified
     3. 下次发送请求的同时,将LastModified一起发送给服务器(由服务器比较内容是否发生变化)
     
     @return 图片资源
     */
    - (void)getData:(GetDataCompletion)completion {
        NSURL *url = [NSURL URLWithString:kLastModifiedImageURL];
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:15.0];
        
        //    // 发送 etag
        //    if (self.etag.length > 0) {
        //        [request setValue:self.etag forHTTPHeaderField:@"If-None-Match"];
        //    }
        // 发送 LastModified
        if (self.localLastModified.length > 0) {
            [request setValue:self.localLastModified forHTTPHeaderField:@"If-Modified-Since"];
        }
        
        [[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
            
            // NSLog(@"%@ %tu", response, data.length);
            // 类型转换(如果将父类设置给子类,需要强制转换)
            NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
            NSLog(@"statusCode == %@", @(httpResponse.statusCode));
            // 判断响应的状态码是否是 304 Not Modified (更多状态码含义解释: https://github.com/ChenYilong/iOSDevelopmentTips)
            if (httpResponse.statusCode == 304) {
                NSLog(@"加载本地缓存图片");
                // 如果是,使用本地缓存
                // 根据请求获取到`被缓存的响应`!
                NSCachedURLResponse *cacheResponse =  [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
                // 拿到缓存的数据
                data = cacheResponse.data;
            }
            
            // 获取并且纪录 etag,区分大小写
            //        self.etag = httpResponse.allHeaderFields[@"Etag"];
            // 获取并且纪录 LastModified
            self.localLastModified = httpResponse.allHeaderFields[@"Last-Modified"];
            //        NSLog(@"%@", self.etag);
            NSLog(@"%@", self.localLastModified);
            dispatch_async(dispatch_get_main_queue(), ^{
                !completion ?: completion(data);
            });
        }] resume];
    }
    

    NSURLRequest默认的cache policy是NSURLRequestUseProtocolCachePolicy, 是最能保持一致性的协议。

    NSURLRequestReloadIgnoringCacheData 忽略缓存直接从原始地址下载

    NSURLRequestReturnCacheDataElseLoad 只有在cache中不存在data时才从原始地址下载

    NSURLRequestReturnCacheDataDontLoad 允许app确定是否要返回cache数据,如果使用这种协议当本地不存在response的时候,创建NSURLConnection or NSURLDownload实例时将会马上返回nil;这类似于离线模式,没有建立网络连接.

    总结

    ETag 是的功能与 Last-Modified 类似:服务端不会每次都会返回文件资源。客户端每次向服务端发送上次服务器返回的 ETag 值,服务器会根据客户端与服务端的 ETag 值是否相等,来决定是否返回 data,同时总是返回对应的 HTTP 状态码。客户端通过 HTTP 状态码来决定是否使用缓存。比如:服务端与客户端的 ETag 值相等,则 HTTP 状态码为 304,不返回 data。服务端文件一旦修改,服务端与客户端的 ETag 值不等,并且状态值会变为200,同时返回 data。

    在官方给出的文档中提出 ETag 是首选的方式,优于 Last-Modified 方式。因为 ETag 是基于 hash ,hash 的规则可以自己设置,而且是基于一致性,是“强校验”。 Last-Modified 是基于时间,是弱校验,弱在哪里?比如说:如果服务端的资源回滚客户端的 Last-Modified 反而会比服务端还要新。

    --
    虽然 ETag 优于 Last-Modified ,但并非所有服务端都会支持,而 Last-Modified 则一般都会有该字段。 大多数情况下需要与服务端进行协调支持 ETag ,如果协商无果就只能退而求其次。

    判断服务器是否支持etag
    [[self.response allHeaderFields] valueForKey:@"ETag"]

    思路
    为资源分派 hash 值,然后对比服务端与本地缓存是否一致来决定是否需要更新缓存。

    这种思路,在开发中经常使用,比如:处于安全考虑,登陆操作一般不会传输账号密码,而是传输对应的 hash 值-- token ,这里的 token 就可以看做一个 file 资源,如果想让一个用户登陆超时时间是三天,只需要在服务端每隔三天更改下 token 值,客户端与服务端值不一致,然后服务端返回 token 过期的提示。

    注意
    如果借助了Last-ModifiedETag,那么缓存策略则必须使用NSURLRequestReloadIgnoringCacheData 策略,忽略缓存,每次都要向服务端进行校验。

    NSURLCache不能满足的情况
    系统帮我们做的缓存,好处是自动,无需我们进行复杂的设置。坏处也恰恰是这个:不够灵活,不能自定义。只能指定一个缓存的总文件夹,不能分别指定每一个文件缓存的位置,更不能为每个文件创建一个文件夹,也不能指定文件夹的名称。缓存的对象也是固定的:只能是 GET请求的返回值。

    瞬间的大图和缩略图缓存问题

    要求 :使用同一个接口 ,在请求头中设置thumbnail 参数来区分 。

    SDWebImageManager *sdmanger = [SDWebImageManager sharedManager];
    [sdmanger.imageDownloader setValue:@"true" forHTTPHeaderField:@"thumbnail"]; 
    

    通过上边给请求设置参数

    [imageView sd_setImageWithURL:@"" placeholderImage:@"" options:SDWebImageHandleCookies|SDWebImageRefreshCached];
    

    在下载图片的时候 ,通过options来控制下载方式 ,因为我们需要额外添加cookes 。所以需要options:SDWebImageDownloaderHandleCookies
    另外需要注意的是 ,sdwebimage默认的是根据url缓存图片 ,所以因为两次请求的url是一样的 ,第二次发送请求高清图的时候并不会重新发送请求,而是从 缓存中提取了第一次的缩略图 。
    所以,options 的选项中还要额外加一个SDWebImageRefreshCached
    这样两次请求 。并不会有两份缓存 。在请求缩略图的时候 ,禁止重复请求 。保证不会让缩略图覆盖掉高清图 。

    sdwebimage 的缓存库是用url 做一次运算以后作为key 。value 是图片的磁盘地址 。地址是用url 和 图片名称 用 md5 提取摘 。

    相关文章

      网友评论

      本文标题:缓存策略

      本文链接:https://www.haomeiwen.com/subject/nknhjttx.html