Java中使用ResponseCache类构建本地缓存

来源:互联网 发布:申根签证截止日期 知乎 编辑:程序博客网 时间:2024/05/22 03:14

《Java网络编程》中介绍了使用Java中的ResponseCache实现本地缓存HTTP请求的效果。例子举得复杂,这里有一个简单的例子可以来说明,而且可以对本地缓存有更深入的理解。

 

use the java.net package formaking HTTP requestsWhile the java.net packageincludes support for caching requests it does not include a default caching implementationand it seemed pretty straightforward to implement theResponseCache, CacheRequest and CacheResponse classes

默认情况下,Java并不完成缓存。要实现本地缓存,需要安装URL类使用的系统级缓存,需要有:ResponseCache,CacheRequest and CacheResponse classes

 

Implementing the Cache

There are three classes toimplement to create a cache that can be used by URLConnection in the java.netpackage. They are ResponseCache, CacheRequest, and CacheResponse. You willcreate your own implementation of these three classes then you will registeryour ResponseCache class to be used as the cache for URLConnections.

 

First extend the request classes:

表示在 ResponseCache 中存储资源的通道。这种类的实例提供一个 OutputStream 对象,协议处理程序可以调用该对象来将资源数据存储到缓存中

CacheRequest is very simple. Ithas one member which is a ByteArrayOutputStream which is returned by itsgetBody() method. The ByteArrayOutputStream will eventually be passed to acompanion CacheResponse instance.

package _07ResponseCache应用实例;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.OutputStream;import java.net.CacheRequest;public class CCCacheRequest extends CacheRequest {final ByteArrayOutputStream body = new ByteArrayOutputStream();@Overridepublic OutputStream getBody() throws IOException {return body;}@Overridepublic void abort() {}

Second extend the response classes:

表示从 ResponseCache 获取资源的通道。这种类的实例提供返回实体正文的 InputStream,同时提供一个返回关联响应头的getHeaders() 方法。

CacheResponse is alsovery simple. Its constructor will accept a Map of HTTP headers, and anOutputStream that we’ll get from a CacheRequest instance but cast to aByteArrayOutputStream. The ByteArrayOutputStream is converted to a byte arrayand passed to the ByteArrayInputStream returned by the CacheRequest’s getBody()method.

package _07ResponseCache应用实例;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.CacheResponse;import java.util.List;import java.util.Map;public class CCCacheResponse extends CacheResponse {Map<String, List<String>> headers;ByteArrayOutputStream body;public CCCacheResponse(Map<String, List<String>> headers, OutputStream body) {this.headers = headers;// should be the output stream defined in a companion CacheRequest.this.body = (ByteArrayOutputStream) body;}@Overridepublic Map<String, List<String>> getHeaders() throws IOException {return headers;}@Overridepublic InputStream getBody() throws IOException {return new ByteArrayInputStream(body.toByteArray());}

Third extend the ResponseCache classes:

表示URLConnection 缓存的实现。

这种类的实例可以通过执行ResponseCache.setDefault(ResponseCache) 向系统注册,系统将调用此对象以便:

l  将从外部源获得的资源数据存储到缓存中

l  试图获取可能存储在缓存中的请求资源

 

ResponseCache defines theHashMap that actually stores the HTTP responses, and creates instances of theCacheRequest and CacheResponse classes.

package _07ResponseCache应用实例;import java.io.IOException;import java.net.CacheRequest;import java.net.CacheResponse;import java.net.ResponseCache;import java.net.URI;import java.net.URLConnection;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.Set;import java.util.Map.Entry;public class CCResponseCache extends ResponseCache {Map<URI, CacheResponse> mCache = new HashMap<URI, CacheResponse>();@Overridepublic CacheResponse get(URI uri, String rqstMethod,Map<String, List<String>> rqstHeaders) throws IOException {CacheResponse resp = mCache.get(uri);return resp;}@Overridepublic CacheRequest put(URI uri, URLConnection conn) throws IOException {CacheRequest req = (CacheRequest)new CCCacheRequest();Map<String, List<String>> headers = conn.getHeaderFields();//将CacheRequest的输出流传递给CacheResponseCacheResponse resp = (CacheResponse) new CCCacheResponse(headers, req.getBody());mCache.put(uri, resp);return req;}public void displayHashMap(){Set<Entry<URI, CacheResponse>> entrySet = mCache.entrySet();for (Entry<URI, CacheResponse> entry : entrySet) {System.out.println("changliang" + entry.getKey()+"--"+entry.getValue());}}}

One thing to note about thisimplementation is that the cache is going to be stored in memory. This is fineso long as you are careful about how much data you store in the cache. If youexpect to cache a large amount of data, it would be better to incorporate adatabase or some kind of file IO into the cache solution.

Last use the locale cahce:

The last step is to create aninstance of the ResponseCache and set it as the default cache via the staticsetDefault() method.
ResponseCache.setDefault(new CCResponseCache());

Now any URLConnection that is set to useCache(true) will use your cache whenmaking HTTP requests. We’re all done right

package _07ResponseCache应用实例;import java.io.BufferedInputStream;import java.io.IOException;import java.io.InputStreamReader;import java.io.Reader;import java.net.MalformedURLException;import java.net.ResponseCache;import java.net.URL;import java.net.URLConnection;import java.util.Scanner;public class CCResponseCacheTest {public static void main(String[] args) {CCResponseCache responseCache = new CCResponseCache();ResponseCache.setDefault(responseCache);while (true) {try {Scanner scanner = new Scanner(System.in);String str = scanner.nextLine();URL url = new URL(str);URLConnection connection = url.openConnection();connection.setUseCaches(true);// 安装系统级缓存System.out.println();responseCache.displayHashMap();System.out.println();BufferedInputStream bis = new BufferedInputStream(connection.getInputStream());Reader reader = new InputStreamReader(bis);int c;int count = 10;while (count >= 0 && (c = reader.read()) != -1) {System.out.print((char) c);count--;}} catch (MalformedURLException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}

至此,对于HTTP请求会缓存到内存的HashMap中。第二次请求相同URL时,不再访问远程服务器,而是直接从HashMap中取出。

由于是个简单示例,有两个重要的方面没有考虑:

1.      应该只是对GET请求进行缓存。这需要在responseCache的put方法中加以控制,如

if (memoryMap.size() >= maxEntries)return null;CacheControl control = new CacheControl(conn.getHeaderField("Cache-Control"));if (control.noStore()) {return null;}else if (!conn.getHeaderField(0).startsWith("GET ")) {// only cache GETreturn null;}

2.      没有缓存失效策略。这需要在responseCache的get方法中加以控制,例如

if ("GET".equals(requestMethod)) {SimpleCacheResponse response = memoryMap.get(uri);// 缓存情况策略// check expiration date  如果过期了 就删除掉 返回null// 这里是将过期的文档从缓存删除之前,会一直等待再次请求这个文档,发现过期了 才会删除  使用Cache-control头部内容if (response != null && response.isExpired()) {memoryMap.remove(response);response = null;}return response;} else {return null;}

附:常见的缓存失效策略

1 FIFO ,first in first out ,最先进入缓存得数据在缓存空间不够情况下(超出最大元素限制时)会被首先清理出去

2 LFU , Less Frequently Used ,一直以来最少被使用的元素会被被清理掉。这就要求缓存的元素有一个hit 属性,在缓存空间不够得情况下,hit 值最小的将会被清出缓存。

3 LRU ,Least Recently Used ,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。

0 0
原创粉丝点击