Java Cache Tutorial with Method Annotations (CDI)

From Resin 4.0 Wiki

(Difference between revisions)
Jump to: navigation, search
(Created page with "{{Caching}} {{Cookbook}} Faster application performance is possible with Java caching by saving the results of long calculations and reducing database load. The Java caching ...")
 
 
Line 1: Line 1:
 
{{Caching}} {{Cookbook}}
 
{{Caching}} {{Cookbook}}
  
Faster application performance is possible with Java caching by saving the results of long calculations and reducing database load. The Java caching API is being standardized with jcache. In combination with Java Dependency Injection (CDI), you can use caching in a completely standard fashion in the Resin Application Server. You'll typically want
+
Java caching can speed application performance and lower database load by annotating cacheable methods. The Java caching API (JCache) includes standard
to look at caching when your application starts slowing down, or your database or other expensive resource starts getting overloaded. Caching is
+
method annotations that let you add caching just by annotating your methods. Assuming your bean is a Java Dependency Injection (CDI) bean, your method will
useful when you want to:
+
be cached automatically. When using an application server like the Resin application server that supports both CDI and JCache, you can add caching easily
 +
without much configuration.
  
 +
@CacheResult is the base annotation for method-based Java caching, and the heart of this tutorial. It uses the method parameters as a cache key, and
 +
stores the method result in the cache. On the next method call, the enhanced method will look for the saved result in the cache, and return it, saving
 +
the effort of the method.
 +
 +
You'll want to cache to 
 
* Improve latency
 
* Improve latency
 
* Reduce database load
 
* Reduce database load
 
* Reduce CPU use
 
* Reduce CPU use
 +
 +
If you want to see how to use the javax.cache.Cache directly, you can look at the [[Java_Cache_Tutorial_with_Cache_Dependency_Injection_(CDI)]] for an example.
 +
 +
= @CacheResult Example =
 +
 +
With the caching annotations, you can add caching with the following two steps:
  
 
# Add a @CacheResult annotation to the method you want to cache
 
# Add a @CacheResult annotation to the method you want to cache
 
# Use Java Dependency Injection (CDI) to get the bean
 
# Use Java Dependency Injection (CDI) to get the bean
 +
 +
In the example, we'll inject the MyBean into a servlet for testing using the CDI @Inject method.
 +
 +
== Using @CacheResult with the action bean ==
  
 
=== MyBean.java ===
 
=== MyBean.java ===
Line 18: Line 34:
 
   public class MyBean {
 
   public class MyBean {
 
     @CacheResult
 
     @CacheResult
     String doLongOperation(String key)
+
     public String doLongOperation(String key)
 
     {
 
     {
 
       ...
 
       ...
 
     }
 
     }
 
   }
 
   }
 +
 +
== Using CDI @Inject in a Servlet ==
  
 
=== MyServlet.java ===
 
=== MyServlet.java ===
Line 41: Line 59:
 
     }
 
     }
 
   }
 
   }
 +
 +
== Enabling CDI Scanning ==
  
 
=== WEB-INF/beans.xml ===
 
=== WEB-INF/beans.xml ===
 
   
 
   
 
   <beans/>
 
   <beans/>
 +
 +
= Performance Benefits of Caching =
 +
 +
Since reducing database load is a typical cache benefit, it's useful to create a micro-benchmark to see how a cache can help. This is just a simple
 +
test with mysql running on the same server and a trivial query. In other words, it's not trying to exaggerate the value of the cache, because almost any
 +
real cache use will have a longer "doLongCalculation" than this simple example, and therefore the cache will benefit even more.
 +
 +
The micro-benchmark has a simple jdbc query in the "doLongCalculation" method
 +
 +
"SELECT value FROM test WHERE id=?"
 +
 +
and then to get useful data, the call to "doStuff" is repeated 300k times and compared with the direct call to "doLongCalculation" 300k times.
 +
 +
<table border='1'>
 +
<tr>
 +
  <th>Type</th>
 +
  <th>Time</th>
 +
  <th>requests per millisecond</th>
 +
  <th>Mysql CPU</th>
 +
</tr>
 +
<tr>
 +
  <th>JDBC</th>
 +
  <td>30s</td>
 +
  <td>10.0 req/ms</td>
 +
  <td>35%</td>
 +
</tr>
 +
<tr>
 +
  <th>Cache</th>
 +
  <td>0.3s</td>
 +
  <td>1095 req/ms</td>
 +
  <td>0%</td>
 +
</tr>
 +
</table>
 +
 +
Even this simple test shows how caches can win. In this simple benchmark, the performance is significantly faster and saves the database load.
 +
 +
* 10x faster
 +
* Remove Mysql load
 +
 +
To get more realistic numbers, you'll need to benchmark the difference on a full application. Micro-benchmarks like this are useful to explain concepts,
 +
but real benchmarks require testing against your own application, in combination with profiling. For example, Resin's simple profiling capabilities
 +
in the /resin-admin or with the pdf-report can get you quick and simple data in your application performance.
 +
 +
== The Resin ClusterCache implementation ==
 +
 +
Since Resin's ClusterCache is a persistent cache, the entries you save will be stored to disk and recovered. This means you can store lots of data in the cache without worrying about running out of memory. (LocalCache is also a persistent cache.) If the memory becomes full, Resin will use the cache entries that are on disk. For performance, commonly-used items will remain in memory.

Latest revision as of 00:00, 28 January 2012

Squirrel-48.pngCookbook-48.png

Java caching can speed application performance and lower database load by annotating cacheable methods. The Java caching API (JCache) includes standard method annotations that let you add caching just by annotating your methods. Assuming your bean is a Java Dependency Injection (CDI) bean, your method will be cached automatically. When using an application server like the Resin application server that supports both CDI and JCache, you can add caching easily without much configuration.

@CacheResult is the base annotation for method-based Java caching, and the heart of this tutorial. It uses the method parameters as a cache key, and stores the method result in the cache. On the next method call, the enhanced method will look for the saved result in the cache, and return it, saving the effort of the method.

You'll want to cache to

  • Improve latency
  • Reduce database load
  • Reduce CPU use

If you want to see how to use the javax.cache.Cache directly, you can look at the Java_Cache_Tutorial_with_Cache_Dependency_Injection_(CDI) for an example.

Contents

@CacheResult Example

With the caching annotations, you can add caching with the following two steps:

  1. Add a @CacheResult annotation to the method you want to cache
  2. Use Java Dependency Injection (CDI) to get the bean

In the example, we'll inject the MyBean into a servlet for testing using the CDI @Inject method.

Using @CacheResult with the action bean

MyBean.java

 package org.example.mypkg;

 public class MyBean {
   @CacheResult
   public String doLongOperation(String key)
   {
     ...
   }
 }

Using CDI @Inject in a Servlet

MyServlet.java

 package org.example.mypkg;

 public class MyServlet extends GenericServlet {
   @Inject MyBean _bean;

   public void service(ServletRequest req, ServletResponse res)
     throws IOException, ServletException
   {
     PrintWriter out = res.getWriter();
 
     String result = _bean.doLongOperation("test");
  
     out.println("test: " + result);
   }
 }

Enabling CDI Scanning

WEB-INF/beans.xml

 <beans/>

Performance Benefits of Caching

Since reducing database load is a typical cache benefit, it's useful to create a micro-benchmark to see how a cache can help. This is just a simple test with mysql running on the same server and a trivial query. In other words, it's not trying to exaggerate the value of the cache, because almost any real cache use will have a longer "doLongCalculation" than this simple example, and therefore the cache will benefit even more.

The micro-benchmark has a simple jdbc query in the "doLongCalculation" method

"SELECT value FROM test WHERE id=?"

and then to get useful data, the call to "doStuff" is repeated 300k times and compared with the direct call to "doLongCalculation" 300k times.

Type Time requests per millisecond Mysql CPU
JDBC 30s 10.0 req/ms 35%
Cache 0.3s 1095 req/ms 0%

Even this simple test shows how caches can win. In this simple benchmark, the performance is significantly faster and saves the database load.

  • 10x faster
  • Remove Mysql load

To get more realistic numbers, you'll need to benchmark the difference on a full application. Micro-benchmarks like this are useful to explain concepts, but real benchmarks require testing against your own application, in combination with profiling. For example, Resin's simple profiling capabilities in the /resin-admin or with the pdf-report can get you quick and simple data in your application performance.

The Resin ClusterCache implementation

Since Resin's ClusterCache is a persistent cache, the entries you save will be stored to disk and recovered. This means you can store lots of data in the cache without worrying about running out of memory. (LocalCache is also a persistent cache.) If the memory becomes full, Resin will use the cache entries that are on disk. For performance, commonly-used items will remain in memory.

Personal tools
TOOLBOX
LANGUAGES