本章介绍通过使用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. [莫队算法 线段树 斐波那契 暴力] Codeforces 633H Fibonacci-ish II

    题目大意:给出一个长度为n的数列a. 对于一个询问lj和rj.将a[lj]到a[rj]从小到大排序后并去重.设得到的新数列为b,长度为k,求F1*b1+F2*b2+F3*b3+...+Fk*bk.当中 ...

  2. 理解和使用 Promise.all 和 Promise.race

    一.Pomise.all的使用 Promise.all可以将多个Promise实例包装成一个新的Promise实例.同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回 ...

  3. 09-spring学习-资源访问接口

    目标: 1,掌握Resource接口的使用. 2,掌握ResourceLoader接口的使用. 3,掌握各种资源数据的读取操作. 具体内容: 要想进行资源读取操作,首先想到IO包中提供的操作类. 但是 ...

  4. hibernate查询排序

    hibernate提供了两种排序方式:1:数据库排序,也就是说通过SQL语句在数据库内部就进行完了排序.2.内存排序,也就是说在数据库中把数据加载到内存中后在进行排序.推荐使用第一种排序方式,因为在数 ...

  5. Java Jaxb JavaBean与XML互转

    1.Jaxb - Java Arcitecture for XML Binding 是业界的一个标准,是一项能够依据XML Schema产生Java类的技术. Jaxb2.0是Jdk1.6的组成部分. ...

  6. Python-理解装饰器

    文章先由stackoverflow上面的一个问题引起吧,如果使用如下的代码: @makebold @makeitalic def say(): return "Hello" 打印出 ...

  7. golang使用sqlite

    安装问题 在import sqlite的时候,golang build 出现以下错误, exec: "gcc": executable file not found in %PAT ...

  8. unity3d之NGUI学习流水账

    博主是跟着视频教程学的,所以最新版的u3d是否已经自带这个功能博主没有考究过. 但是视频是2015下半年的教程,当时的u3d还是需要自行导入NGUI包的. 1.首先需要下载NGUI包.点此进入ngui ...

  9. mongod启动时候几个参数说明

    1.--storageEngine从3.0开始,新增了一个存储引擎WT.老版本的存储引擎被称作mmapv1 2.--oplogSizeOplog是复制的主要组成部分,位于local数据库中.在初始化复 ...

  10. Memcached管理与监控

    一个用PHP编写的可视化的MemCached管理系统 MemAdmin是一款可视化的Memcached管理与监控工具,使用PHP开发,体积小,操作简单. 主要功能: 服务器参数监控:STATS.SET ...