11

假设我有以下使用@GET方法的 Web 服务调用:

@GET
@Path(value = "/user/{id}")
@Produces(MediaType.APPLICATION_JSON)
public Response getUserCache(@PathParam("id") String id, @Context HttpHeaders headers) throws Exception {
    HashMap<String, Object> map = new HashMap<String, Object>();
    map.put("id", id);
    SqlSession session = ConnectionFactory.getSqlSessionFactory().openSession();
    Cre8Mapper mapper = session.getMapper(Cre8Mapper.class);

    // slow it down 5 seconds
    Thread.sleep(5000);

    // get data from database
    User user = mapper.getUser(map);

    if (user == null) {
        return Response.ok().status(Status.NOT_FOUND).build();
    } else {
        CacheControl cc = new CacheControl();
        // save data for 60 seconds
        cc.setMaxAge(60);
        cc.setPrivate(true);
        return Response.ok(gson.toJson(user)).cacheControl(cc).status(Status.OK).build();
    }
}   

为了进行实验,我在从数据库中获取数据之前将当前线程放慢了 5 秒。当我使用Firefox Poster
调用我的网络服务时,在 60 秒内,第 2 次、第 3 次调用等似乎要快得多,直到超过 60 秒。 但是,当我将 URI 粘贴到浏览器(Chrome)时,它似乎每次都会减慢 5 秒。而且我真的很困惑如何使用这种技术实际完成缓存。以下是我的问题:

  1. POSTER 是否真的查看标题max-age并决定何时获取数据?
  2. 在客户端(web、android ....)中,当访问我的 web 服务时,我需要检查标头然后手动执行缓存还是浏览器已经缓存了数据本身?
  3. 有没有办法避免每次都从数据库中获取数据?我想我必须以某种方式将我的数据存储在内存中,但它可能会耗尽内存吗?
  4. 在本教程 中 JAX-RS 缓存教程:缓存实际上是如何工作的?第一行总是从数据库中获取数据:

    Book myBook = getBookFromDB(id);

那么它是如何被认为是缓存的呢?除非代码不按自上而下的顺序执行。

    @Path("/book/{id}")
    @GET
    public Response getBook(@PathParam("id") long id, @Context Request request) {
        Book myBook = getBookFromDB(id);
        CacheControl cc = new CacheControl();
        cc.setMaxAge(86400);
        EntityTag etag = new EntityTag(Integer.toString(myBook.hashCode()));        
        ResponseBuilder builder = request.evaluatePreconditions(etag);
        // cached resource did change -> serve updated content
        if (builder == null){
            builder = Response.ok(myBook);
            builder.tag(etag);
        }
        builder.cacheControl(cc);
        return builder.build();
    } 
4

2 回答 2

9

从您的问题中,我看到您将客户端缓存(http)与服务器端缓存(数据库)混合在一起。我认为造成这种情况的根本原因是您首先在 Firefox 和 chrome 中观察到的不同行为,我将尝试清除它

当我使用 Firefox Poster 调用我的 Web 服务时,在 60 秒内,第 2 次、第 3 次调用等似乎要快得多,直到超过 60 秒。但是,当我将 URI 粘贴到浏览器(Chrome)时,它似乎每次都会减慢 5 秒。

例子 :

 @Path("/book")
    public Response getBook() throws InterruptedException {
        String book = " Sample Text Book";
        TimeUnit.SECONDS.sleep(5); // thanks @fge
        final CacheControl cacheControl = new CacheControl();
        cacheControl.setMaxAge((int) TimeUnit.MINUTES.toSeconds(1)); 
        return Response.ok(book).cacheControl(cacheControl).build();
    }

我有一个安静的网络服务正在运行,这个网址是

http://localhost:8780/caching-1.0/api/cache/book - GET

火狐:

当我第一次访问 url 时,浏览器向服务器发送了请求,并得到了带有缓存控制标头的响应。

fiefox 初始请求

60 秒内的第二个请求(使用 Enter):这次 firefox 没有去服务器获取响应,而是从缓存中加载数据

在此处输入图像描述

60 秒后的第三个请求(使用 Enter):

这次firefox向服务器发出请求并得到响应。

使用刷新(F5 或 ctrl F5)的第四个请求:

如果我在前一个请求的 60 秒内刷新页面(而不是按回车键),Firefox 没有从缓存中加载数据,而是向服务器发出请求,请求中带有特殊标头

在此处输入图像描述

铬合金 :

60 秒内的第二个请求(使用 Enter ):这次 chrome 再次向服务器发送请求,而不是从缓存中加载数据,并在请求中添加了标头cache-control = "max-age=0"

汇总结果:

由于 chrome 对输入点击的响应不同,您在 firefox 和 chrome 中看到了不同的行为,它与 jax-rs 或您的 http 响应无关。总而言之,客户端(firefox/chrome/safari/opera)将在缓存控制中缓存指定时间段的数据,除非时间到期或我们执行强制刷新,否则客户端不会向服务器发出新请求。

我希望这能澄清你的问题 1,2,3。

4.在本教程中 JAX-RS 缓存教程:缓存实际上是如何工作的?第一行总是从数据库中获取数据:

Book myBook = getBookFromDB(id);

那么它是如何被认为是缓存的呢?除非代码不按自上而下的顺序执行。

您提到的示例不是在谈论最小化数据库调用,而是关于通过网络节省带宽,客户端已经有数据,并且如果数据更新或没有数据更新,则客户端检查服务器(重新验证)以响应您'重新发送实际实体。

于 2014-06-09T20:21:13.197 回答
1
  1. 是的。

  2. 当使用像 firefox 或 chrome 这样的浏览器时,您无需担心 HTTP 缓存,因为现代浏览器会处理它。例如,它在使用 Firefox 时使用内存缓存。使用 Android 时,这取决于您与源服务器的交互方式。根据WebView,它实际上是一个浏览器对象,但是如果使用HTTPClient,则需要自己处理HTTP缓存。

  3. 这与 HTTP 缓存无关,而是您的服务器端逻辑。常见的答案是使用数据库缓存,这样您就不需要在每个 HTTP 请求中都访问数据库。

  4. 实际上,JAX-RS 只是为您提供了使用 HTTP 缓存标头的方法。您需要使用 CacheControl 和/或 EntityTag 来执行基于时间的缓存和条件请求。例如,当使用 EntityTag 时,构建器将处理您无需担心的响应状态代码 304。

于 2013-07-07T14:22:57.437 回答