【问题标题】:How does caching work in JAX-RS?缓存在 JAX-RS 中是如何工作的?
【发布时间】:2013-07-05 05:44:35
【问题描述】:

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

@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 调用我的 Web 服务时,在 60 秒内,第二次、第三次调用等等似乎要快得多,直到超过 60 秒。
但是,当我将 URI 粘贴到浏览器(Chrome)时,它似乎每次都会减慢 5 秒。而且我真的很困惑如何使用这种技术实际完成缓存。以下是我的问题:

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

    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();
    } 

【问题讨论】:

  • 旁注,不回答问题:您可以使用86400 代替TimeUnit.DAYS.toSeconds(1)。在眼睛上更容易,不需要解码工作;)
  • 同样,你可以使用TimeUnit.SECONDS.sleep(5),顺便说一句
  • 关于您的问题,您是否分析了到达浏览器的标头?看起来是浏览器的行为不同,我不相信任何 JAX-RS 实现都会为不同的浏览器产生不同的标头,但是...
  • Poster 确实依赖于 Firefox 缓存机制。但是您需要 firebug 网络选项卡来查看您的 GET 请求是否被缓存。

标签: java web-services http jax-rs http-caching


【解决方案1】:

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

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

例子:

 @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();
    }

我有一个安静的 web 服务正在运行,它的 url 是

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

火狐:

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

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

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

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

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

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

铬:

Second Request with in 60 seconds (using Enter ) : 这次 chrome 再次向服务器发送请求,而不是从缓存中加载数据,并且在请求中添加了 header 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);

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

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

【讨论】:

    【解决方案2】:
    1. 是的。

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-04-25
      • 2011-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-01
      相关资源
      最近更新 更多