| ||||||||||
Resin 3.1 Documentation Examples Changes Overview Installation Configuration Quercus SOA/IoC JSP Servlets and Filters Admin (JMX) EJB Amber Security Performance Hessian XML and XSLT Third-party Troubleshooting/FAQ JVM Tuning Proxy Caching Performance FAQ Performance Scrapbook |
Server caching can speed dynamic pages to near-static speeds. Many pages created by database queries only change every 15 minutes or so, e.g. CNN or Slashdot. Resin can cache the results and serve them like static pages. Resin's caching will work for any servlet, including JSP and XTP pages. It depends only on the headers the servlet returns in the response. By default, pages are not cached. To cache, a page must set a HTTP caching header. Resin's caching operates like a proxy cache. It's controlled by the same HTTP headers as any proxy cache. Every user shares the same cached page. Cache-Control: max-ageSetting the header will cache the results for a specified time. For heavily loaded pages, even setting short expires times can significantly improve performance.Note, pages using sessions should not be cached, although more sophisticated headers like "Cache-Control: private" can specify caching only for the session's browser. The following example sets expiration for 15 seconds. So the counter should update slowly. <%@ page session="false" %> <%! int counter; %> <% response.addHeader("Cache-Control", "max-age=15"); %> Count: <%= counter++ %> is useful for database generated pages which are continuously, but slowly updated. To cache based on something with a known modified date, like a file, you can use . Debugging cachingWhen designing and testing your cached page, it's important to see how Resin is caching the page. To turn on logging for caching, you'll add the following to your resin.conf: <log name="com.caucho.server.cache" path="log/cache.log" level="fine"/> The output will look something like the following: [10:18:11.369] caching: /images/caucho-white.jpg etag="AAAAPbkEyoA" length=6190 [10:18:11.377] caching: /images/logo.gif etag="AAAAOQ9zLeQ" length=571 [10:18:11.393] caching: /css/default.css etag="AAAANzMooDY" length=1665 [10:18:11.524] caching: /images/pixel.gif etag="AAAANpcE4pY" length=61 ... [2003/09/12 10:18:49.303] using cache: /css/default.css [2003/09/12 10:18:49.346] using cache: /images/pixel.gif [2003/09/12 10:18:49.348] using cache: /images/caucho-white.jpg [2003/09/12 10:18:49.362] using cache: /images/logo.gif ExpiresAn application can also set the header to enable caching, when the expiration date is a specific time instead of an interval. For heavily loaded pages, even setting short expires times can significantly improve performance. Sessions should be disabled for caching.The following example sets expiration for 15 seconds. So the counter should update slowly. <%@ page session="false" %> <%! int counter; %> <% long now = System.currentTimeMillis(); response.setDateHeader("Expires", now + 15000); %> Count: <%= counter++ %> is useful for database generated pages which are continuously, but slowly updated. To cache based on something with a known modified date, like a file, you can use . If-ModifiedThe headers let you cache based on an underlying change date. For example, the page may only change when an underlying source page changes. Resin lets you easily use by overriding methods in HttpServlet or in a JSP page.The following page only changes when the underlying 'test.xml' page changes. <%@ page session="false" %> <%! int counter; public long getLastModified(HttpServletRequest req) { String path = req.getRealPath("test.xml"); return new File(path).lastModified(); } %> Count: <%= counter++ %> pages are useful in combination with the configuration. ServletsCaching servlets is exactly like caching JSP pages (or XTP or static files.) Resin's caching mechanism works like a proxy cache: it don't care how the page is generated; as long as the proper caching headers are set, the page will be cached. package test; import javax.servlet.*; import javax.servlet.http.*; import java.io.*; public class TestServlet extends HttpServlet { int counter; public long getLastModified(HttpServletRequest req) { String path = req.getRealPath("test.xml"); return new File(path).lastModified(); } public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { PrintWriter out = res.getWriter(); out.println("Count: " + counter++); } } Sessions and CookiesBecause Resin follows the HTTP caching spec, setting caching on a page which relies on sessions will make the page cacheable. You can test this by hitting the following page with separate browsers. <% response.addHeader("Cache-Control", "max-age=3600"); %> session id <%= session.getId() %> Normally, this behavior is not what you want. Instead, you may want
the browser to cache the page, but not let other browsers
see the same page. To do that, you'll set
the "Cache-Control: private" header. You'll need to use <% response.addHeader("Cache-Control", "max-age=3600"); response.addHeader("Cache-Control", "private"); %> session id <%= session.getId() %> Included PagesResin can cache subpages even when the top page can't be cached. Sites allowing user personalization will often design pages with subpages. Some subpages are user-specific and can't be cached. Others are common to everybody and can be cached.Resin treats subpages as independent requests, so they can be cached independent of the top-level page. Try the following, use the first counter example as the included page. Create a top-level page that looks like:<% if (! session.isNew()) { %> <h1>Welcome back!</h1> <% } %> <jsp:include page="expires.jsp"/> VaryIn some cases, you'll want to have separate cached pages for the same URL depending on the capabilities of the browser. Using gzip compression is the most important example. Browsers which can understand gzip-compressed files receive the compressed page while simple browsers will see the uncompressed page. Using the "Vary" header, Resin can cache different versions of that page. <% response.addHeader("Cache-Control", "max-age=3600"); response.addHeader("Vary", "Accept-Encoding"); %> Accept-Encoding: <%= request.getHeader("Accept-Encoding") %> Caching Anonymous UsersIn many cases, logged in users get specialized pages, but anonymous users all see the same page. In this case, you can still take advantage of Resin's caching, but you'll need to do a little work in your design. First, you'll need to create an subpage that contains the common page. The top page can't be cached because it depends on whether a user is logged in or not.You must use because is cached just like the top page. The top page isn't cacheable because of the user login, so the forwarded page isn't cacheable either.Here's what a sample subpage might look like: <%@ page session=false %> <%! int counter; %> <% long now = System.currentTimeMillis(); response.setDateHeader("Expires", now + 15000); String user = request.getParameter("user"); %> User: <%= user %> <%= counter++ %> The top page slightly trickier because it needs to pass the user to the subpage. You need to pass a unique id. If you pass a boolean parameter, all logged in users will see the same page.<%@ page session=true %> <% String user = getSomeSortOfUniqueUserId(); if (user == null) user = "Anonymous"; %> ... <jsp:include page='cachedpage.jsp'/> <jsp:page name='user' value='<%= user %>'/> </jsp:include> Of course, the top-level page could also be a servlet: ... String user = getSomeSortOfUniqueUserId(request); if (user == null) user = "Anonymous"; RequestDispatcher disp; disp = request.getRequestDispatcher("/cachedpage.jsp?user=" + user); disp.include(request, response); Experimental Anonymous CachingResin includes an anonymous user caching feature. If a user is not logged in, she will get a cached page. If she's logged in, she'll get her own page. This feature will not work if anonymous users are assigned cookies for tracking purposes. To make anonymous caching work, you must set the header. If you omit the x-anonymous header, Resin will use the to cache the same page for every user.<%@ page session="false" %> <%! int counter; %> <% response.addHeader("Cache-Control", "max-age=15"); response.addHeader("Cache-Control", "x-anonymous"); String user = request.getParameter("user"); %> User: <%= user %> <%= counter++ %> The top page must still set the or header, but Resin will take care of deciding if the page is cacheable or not. If the request has any cookies, Resin will not cache it or use the cached page. If it has no cookies, Resin will use the cached page.When using x-anonymous, user tracking cookies will make the page uncacheable even if the page is the same for all users. Resin chooses to cache or not based on the existence of any cookies in the request, whether they're used or not. cache-mappingassigns a and to an If-Modified cacheable page. It does not affect or cached pages. The FileServlet takes advantage of because it's an If-Modified servlet. Often, you want a long Expires time for a page to a browser. For example, any gif will not change for 24 hours. That keeps browsers from asking for the same gif every five seconds; that's especially important for tiny formatting gifs. However, as soon as that page or gif changes, you want the change immediately available to any new browser or to a browser using reload. Here's how you would set the Expires to 24 hours for a gif, based on the default FileServlet. <web-app id='/'> <cache-mapping url-pattern='*.gif' expires='24h'/> </web-app> The automatically generates the Expires header. It only works for cacheable pages setting If-Modified or ETag. It will not affect pages explicily setting Expires or non-cacheable pages. So it's safe to create a cache-mapping for even if only some are cacheable.
|