本章介绍通过使用Ali Kheyrollahi开发的CacheCow来实现服务器端的缓存。所有代码现在都可以在GitHub上下载。

我们将要实现的缓存方式叫做Conditional Requests,实现方式其实很简单。客户端通过在请求Header中包含ETag信息,实现和服务器的交互,确认当前客户端包含的部分数据是否已经被修改,有则拉取,没有服务器端就返回304(Not Modified)和空的消息体。通过Conditional Requests,客户端不断的发起请求,但只有客户端数据过期时才会返回200和对应的数据信息。

什么是ETag(Entity Tag)?

ETag有一个基于String的惟一键,服务端为每种类型的资源单独生成,可以看做是资源的校验位,用来控制对应的资源是否被修改。

ETag有强标识和弱标识两种,弱标识以W开头,形如“W/53fsfsd322”;强标识一般形如“534928jhfr4”。弱标识一般标明缓存的资源只会被使用一小段时间(In Momory Caching)。而强标识则意味着资源会被持久化,资源在客户端和服务端都是完全相同(字节相等,byte-per-byte identical)的。(注:留待查明,作者的说法可能有问题,文章最后提到了一些不同的观点)

ETag的原理

下图展示了一个过程,当客户端通过HTTP GET请求Id为4的course资源时,如果该资源从未被请求,那么服务端直接返回该资源并在Response Header中添加了一个ETag标识。

如果客户端再次请求统一资源,可以再GET请求中包含一个叫做If-None-Match的Header,其值为对应的ETag。当服务器收到时,就会进行匹配。如果未改动,返回304和空的消息体。否则,返回最新的记录。

GET和DELETE都可以使用这个If-None-Match的Header,但是更新资源时如果要用到ETag,需要在对应的PUT或者PATCH请求中使用If-Match的Header,服务器同样会对ETag进行检查。如果服务端资源信息已被更新,就会返回412(Precondition Failed),客户端需要进行必要的提示和处理,本次修改将不能被服务端接受。

配置CacheCow

从Nuget控制台可以通过命令Install-Package CacheCow.Server -Version 0.4.12获取到CacheCow的最新版本。包含两个dll文件。

配置的过程就是创建一个CacheHandler实例,并将其诸如到Web API的处理管线中,这个Handler将用来对每个请求进行拦截,并对Response进行必要的处理。

   1:  //Configure HTTP Caching using Entity Tags (ETags)
   2:  var cacheCowCacheHandler = new CacheCow.Server.CachingHandler();
   3:  config.MessageHandlers.Add(cacheCowCacheHandler);

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

到目前为止,我们的API可以实现基于内存的缓存,这也是CacheCow的默认配置。对于单个服务器或者演示,而言已经足够。但是如果要处于负载均衡或WebFarm状态时,缓存状态必须单独存储,并在不同的服务器间进行共享,因此需要配置额外的存储介质,如SQL Server,MongoDB,MemCache等。

基于内存的缓存测试

进行后续操作前,先对请求进行一个测试。打开Fiddler,选择Composer选项卡,构造一个GET请求,类似于:http://localhost:{your_port}/api/courses/4 ,请求的结果如下:

你应该注意到这些:

  1. 返回状态码为200,表示服务端在消息体返回了资源的内容;

  2. 有两个新的Header被添加到Response中;注意,后续我将介绍将Last-Modified头去掉以免互相影响;
  3. 图中的ETag是弱标识类型(以W开头),标示数据缓存在内存中,一旦IIS重启,缓存会丢失

接下来,对请求进行模拟。在Headers中添加If-None-Match,执行后的结果如下:

值得注意的是,返回的ETag标识和传入的标识是相同的。

通过SQL Server进行缓存

这种方式需要安装必要的dll,在NuGet控制台中通过“Install-Package CacheCow.Server.EntityTagStore.SqlServer -Version 0.4.11”进行安装,然后修改WebApiConfig文件如下:

   1:  //Configure HTTP Caching using Entity Tags (ETags)
   2:  var connString = System.Configuration.ConfigurationManager.ConnectionStrings["eLearningConnection"].ConnectionString;
   3:  var eTagStore = new CacheCow.Server.EntityTagStore.SqlServer.SqlServerEntityTagStore(connString);
   4:  var cacheCowCacheHandler = new CacheCow.Server.CachingHandler(eTagStore);
   5:  cacheCowCacheHandler.AddLastModifiedHeader = false;
   6:  config.MessageHandlers.Add(cacheCowCacheHandler);

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

我们向CacheHandler中传递了数据库的信息,并去掉了LastModifiedHeader。如果你在这个时候测试请求,会接收到一个500错误(Internal Server Error),因为我们还需要创建必要的存储过程和表。定位到NuGet包的位置,一般类似于“{projectpath}packagesCacheCow.Server.EntityTagStore.SqlServer.0.4.11scripts”,然后再数据库中执行即可。

进行上文类似的测试后,检索CacheState表,可以看到如下信息:

接下来再发生的GET请求都会直接返回304,知道数据被修改。现在我们接着对数据进行Update,返回情况如下图。

这个PUT请求分析如下:

  1. 返回状态码为200,意味着客户端的数据是最新的,并且更新也被正确的执行了;

  2. 返回了新的ETag,因为服务端的数据已经被修改;客户端接下来的访问都必须使用新的ETag;
  3. CacheStore记录了新的ETag值和Last Modified Date。

如果再次执行刚才的请求,将会直接返回412错误。

来自评论和思考

简单说,这种缓存方式对单个或少量的API十分有效。但是,如果存在拉取一个大的列表如/api/courses这样,就会比较难以处理。特别的,请求返回时,对逐个对象进行判断也是十分费事的。作者建议对此类的API关闭缓存,然后通过AttributeBasedCancheControlPolicy对单一的请求进行标识。

另外,有个人提到W/其实并不标识缓存的存储介质类型,他用来标识数据是语义相等,而强标识则意味着字节相等。这个人还提出了一套基于ActionFilter的解决方案

系列到此结束,完整代码现在都可以在GitHub上下载。

来源:http://bitoftech.net/2014/02/08/asp-net-web-api-resource-caching-etag-cachecow/

[翻译]创建ASP.NET WebApi RESTful 服务(11)的更多相关文章

  1. [翻译]创建ASP.NET WebApi RESTful 服务(8)

    本章讨论创建安全的WebApi服务,到目前为止,我们实现的API都是基于未加密的HTTP协议,大家都知道在Web中传递身份信息必须通过HTTPS,接下来我们来实现这一过程. 使用HTTPS 其实可以通 ...

  2. [翻译]创建ASP.NET WebApi RESTful 服务(9)

    一旦成功的发布API后,使用者将依赖于你所提供的服务.但是变更总是无法避免的,因此谨慎的制定ASP.NET Web API的版本策略就变得非常重要.一般来说,新的功能需要无缝的接入,有时新老版本需要并 ...

  3. [翻译]创建ASP.NET WebApi RESTful 服务(7)

    实现资源分页 本章我们将介绍几种不同的结果集分页方式,实现手工分页,然后将Response通过两个不同的方式进行格式化(通过Response的Envelop元数据或header). 大家都知道一次查询 ...

  4. [翻译]创建ASP.NET WebApi RESTful 服务(10)

    通过URI实现版本管理 另一种实现版本管理的方式就是通过URI来进行处理,类似于http://localhost:{your_port}/api/v1/students/.这种方式的好处是使用者可以清 ...

  5. IIS 部署ASP.Net, WebAPI, Restful API, PUT/DELETE 报405错解决办法, webapi method not allowed 405

    WebDAV 是超文本传输协议 (HTTP) 的一组扩展,为 Internet 上计算机之间的编辑和文件管理提供了标准.利用这个协议用户可以通过Web进行远程的基本文件操作,如拷贝.移动.删除等.在I ...

  6. Asp.net WebAPi Restful 的实现和跨域

    现在实际开发中用webapi来实现Restful接口开发很多,我们项目组前一段时间也在用这东西,发现大家用的还是不那么顺畅,所以这里写一个Demo给大家讲解一下,我的出发点不是如何实现,而是为什么? ...

  7. 在windows10上创建ASP.NET mvc5+Memcached服务

    感谢两位两位大佬: https://blog.csdn.net/l1028386804/article/details/61417166 https://www.cnblogs.com/running ...

  8. 让Asp.Net WebAPI支持OData查询,排序,过滤。

    让Asp.Net WebAPI支持OData后,就能支持在url中直接输入排序,过滤条件了. 一.创建Asp.Net WebAPI项目: 二.使用NuGet安装Asp.Net WebAPI 2.2和O ...

  9. ASP.NET WebAPI 双向token实现对接小程序登录逻辑

    最近在学习用asp.net webapi搭建小程序的后台服务,因为基于小程序端和后台二者的通信,不像OAuth(开放授权),存在第三方应用.所以这个token是双向的,一个是对用户的,一个是对接口的. ...

随机推荐

  1. 算法笔记_087:蓝桥杯练习 9-1九宫格(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 九宫格.输入1-9这9个数字的一种任意排序,构成3*3二维数组.如果每行.每列以及对角线之和都相等,打印1.否则打印0. 样例输出 与上面 ...

  2. CentOS下febootstrap自制Docker的CentOS6.6和7.1 Docker镜像

    docker image centos febootstrap CentOS 6.6和7.1 Docker自制CentOS镜像 安装: ? 1 yum -y install febootstrap 添 ...

  3. linux安装php sphinx出错

    安装sphinx的php客户端 # wget -c http://pecl.php.net/get/sphinx-1.3.0.tgz # .tgz # cd sphinx- # phpize # ./ ...

  4. 详解Struts1中的struts-config.xml配置文件【一】

    搞清楚struts-config.xml中各项元素的作用,对于我们构建web项目有莫大的好处.<struts-config>是struts的根元素,它主要有8个子元素,DTD定义如下: & ...

  5. 探寻BTree 索引对sql 优化影响

    从一道题開始分析: 如果某个表有一个联合索引(c1,c2,c3,c4)一下--仅仅能使用该联合索引的c1,c2,c3部分 A where c1=x and c2=x and c4>x and c ...

  6. C#使用技巧之调用JS脚本(转)

    .创建个Winform项目. .在From1上增加一个文本框一个按钮. .在解决方案中创建一个test.js文件. test.js代码如下: function sayHello(str) { retu ...

  7. laydate 和 Vue 奇怪的清空问题

    laydate的input,会自动被清空,当别的input修改的时候.改成这样既可解决 <td><input type="text" id="retur ...

  8. 运行百度语音识别官方iOS demo,无法离线识别解决办法

    需对demo进行如下修改: 1,我下载了一个临时授权文件temp_license_2015-10-27,把它拖到xcode工程里. 2,然后在BDVRViewController.m中的loadOff ...

  9. 定时检测Memcached进程是否存在,若不存在自动启动它

    由于一台WEB服务器的Memcached死掉而导致在访问网站的某些页面时候打不开.下面脚本会自动检测Memcached的进程,如果挂掉则自动重启Memcached服务. vim memcached_c ...

  10. Java中的synthetic

    有synthetic标记的field和method是class内部使用的,正常的源代码里不会出现synthetic field.小颖编译工具用的就是jad.所有反编译工具都不能保证完全正确地反编译cl ...