本章介绍通过使用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. 1: //Configure HTTP Caching using Entity Tags (ETags)
  1. 2: var cacheCowCacheHandler = new CacheCow.Server.CachingHandler();
  1. 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. 1: //Configure HTTP Caching using Entity Tags (ETags)
  1. 2: var connString = System.Configuration.ConfigurationManager.ConnectionStrings["eLearningConnection"].ConnectionString;
  1. 3: var eTagStore = new CacheCow.Server.EntityTagStore.SqlServer.SqlServerEntityTagStore(connString);
  1. 4: var cacheCowCacheHandler = new CacheCow.Server.CachingHandler(eTagStore);
  1. 5: cacheCowCacheHandler.AddLastModifiedHeader = false;
  1. 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. Win7如何设置标准账号无法访问我的D盘

    1 右击对应的盘符,选择属性 2 设置对应的权限,比如是否可读,是否可写 3 之后再用这种用户登录就可以知道相应的权限了

  2. P2P终结者和反P2P终结者如何使用

    1 安装软件并运行,首先扫描网络,第一台控制机就是自己,你可以查看IP,和命令提示符下的IP吻合. 2 点击高级选项,指定本机网络环境和网卡 3 控制规则设置,首先设置全局限速模板,其他的差不多. 4 ...

  3. org.eclipse.e4.core.di.InjectionException:org.eclipse.swt.SWTException: Widget is disposed

    org.eclipse.e4.core.di.InjectionException:org.eclipse.swt.SWTException: Widget is disposed 开发环境为ecli ...

  4. Loadrunner关于页面检查的几个函数详解

    环境:Loadrunner版本:8.0自建一个test.html文件:<html><head><meta name="google1" content ...

  5. array_intersect_assoc用法详解

    最近在做考试系统,想到这个数组函数,用法如下: <?php $a1=array('a','b','d','c','d','b','c','a'); $a2=array('b','d','d',' ...

  6. Java学习笔记3、变量、数据类型

    标识符 常见的命名规则(见名知意) 包名全部小写 类或者接口,一个单词:首字母大写,多个单词:每个单词首字母大写. 方法或者变量:一个单词:首字母小写,多个单词:从第二个单词开始,每个单词首字母大写. ...

  7. <译>Flink编程指南

    Flink 的流数据 API 编程指南 Flink 的流数据处理程序是常规的程序 ,通过再流数据上,实现了各种转换 (比如 过滤, 更新中间状态, 定义窗口, 聚合).流数据可以来之多种数据源 (比如 ...

  8. 08-hibernate注解-多对多单向外键关联

    多对多单向外键 1,学生和教师构成多对多的关联关系 2,其中一个多方持有另一个多方的集合对象(学生持有教室的集合) 3,通过注解@JoinTable,创建中间表(作为多对多的载体,用来确定学生和教师的 ...

  9. 特殊文件权限(setuid、setgid 和 Sticky 位)

    可执行文件和公共目录可以使用三种特殊类型的权限:setuid.setgid 和 sticky 位.设置这些权限之后,运行可执行文件的任何用户都应采用该可执行文件属主(或组)的 ID. setuid 权 ...

  10. Random.org -- 真正的随机数生成器

    接触过程序设计的人一定对随机数不陌生.随机数的用途非常广,比方在測试.分布和统计程序.游戏中. 大多数编程语言也提供了随机数库.能够方便的使用. 只是从严格意义上来讲,这些程序生成的随机数并非真正的随 ...