Http缓存
过滤器是非常有用的,比如Http缓存协议的实现就可以基于过滤器来做。
在AndServer中,有一个HttpCacheFilter
类,它实现了Http缓存协议部分,下面是它实现的几个关键头:
- Cache-Control
- Last-Modified
- If-Modified-Since
- If-Unmodified-Since
- ETag
- If-None-Match
用法
比如我们100%建议开启Website
的缓存,它能提高用来提高服务器性能,尤其像AndServer
这种运行在手机端的服务器。
Server server = AndServer.serverBuilder()
...
.filter(new HttpCacheFilter())
.build();
...
如上述代码即可打开AndServer自带的几个Website
的缓存:
- AssetsWebsite
- StorageWebsite
Http缓存协议
我们先来认识AndServer中的两个Interface:
- LastModified,用来支持响应头的
Last-Modified
和请求头的If-Modified-Since
- ETag,用来支持响应头的
ETag
和请求头的If-None-Match
关于上述两个Http头的概念,如果大家还有疑问请Google搜索相关资料了解。
每个接口的数据肯定是又唯一属性的,所以要保证每一个接口的数据都有一个唯一ID。LastModified
接口是返回当前接口指向资源的修改时间的,ETag
接口是返回当前接口指向资源的唯一值的。唯一值这个概念可能比较难理解,因为Http没有规定ETag
的值是资源的什么属性,比如HashCode
、MD5
等等都可以作为一个唯一属性。
LastModified
此接口返回当前资源的修改时间(Last-Modified
响应头),客户端检测到这个响应头时会缓存当前接口的响应数据。客户端再次请求当前资源时,会带上上次请求此接口返回的修改时间(If-Modified-Since
相应头),服务器会用If-Modified-Since
和服务器上资源的修改时间做对比。如果If-Modified-Since
大于等于服务器资源的修改时间,那么返回304响应码,没有任何响应包体,客户端将会使用上次缓存的数据;如果If-Modified-Since
小于服务器资源的修改时间,说明自上次请求之后,此资源已经被修改过了,也就是服务器的资源比客户端的资源新,服务器此时返回响应码200,返回新的资源作为响应包体。
ETag
一般情况下我们只要开启LastModified
即可,那么ETag又是做什么的呢?因为Unix时间戳是按照秒计算的,所以Http中的时间都是秒,或者说表示毫秒的三位都是0。由此可见,如果当前接口指向的资源在1秒内被修改,那么客户端将拿不到最新的资源。所以介于此,Http添加了ETag来兼容Last-Modified
,Http协议固定ETag
的值应该是粒度的值,也就是说它比Last-Modified
更加细化。
RequestHandler如何使用缓存
如果开发者设置了HttpCacheFilter
为AndServer的过滤器,那么为RequestHandler
开启缓存功能将会很简单,不用自己在Response
中设置任何头。
public class MyHandler implement RequestHandler, LastModified {
public void handle(HttpRequest request, HttpResponse response, HttpContext context)
throws HttpException, IOException {
// 比如当前资源是个File。
String path = HttpRequestParser.getRequestPath(request);
... // 一些判断此资源是个文件。
File file = new File(path);
response.setStatusCode(200);
response.setEntity(new FileEntity(file));
}
@Ovrride
public long getLastModified(HttpRequest request) ... {
// 比如当前资源是个File。
String path = HttpRequestParser.getRequestPath(request);
... // 一些判断此资源是个文件。
File file = new File(path);
return file.lastModified();
}
}
比如对某个文件做了缓存支持,以当前文件的Last-Modified
为唯一性的判断依据还不够,那么我们应该实现ETag
接口,返回这个文件的更详细的唯一属性。
关于ETag的建议:建议返回当前文件的MD5值,并做一些格式上的处理,这样基本可以做到99.99%的唯一性。当文件的内容被修改(包括在1秒内修改)时,文件的MD5会立刻发生变化。当然对于普通接口也是一样,可以返回要返回内容的MD5值(或者其它粒度唯一属性也可以)。
public class MyHandler implement RequestHandler, LastModified {
public void handle(HttpRequest request, HttpResponse response, HttpContext context)
throws HttpException, IOException {
// 同上...
}
@Ovrride
public long getLastModified(HttpRequest request) throws HttpException, IOException {
// 同上...
}
@Ovrride
public String getETag(HttpRequest request) throws HttpException, IOException {
// 比如当前资源是个File。
String path = HttpRequestParser.getRequestPath(request);
... // 一些判断此资源是个文件。
File file = new File(path);
// 返回当前文件的MD5值。
InputStream inStream = new FileInpuStream(file);
return DigestUtils.md5DigestAsHex(inStream);
}
}