blogger templates blogger widgets
Showing posts with label caching. Show all posts
Showing posts with label caching. Show all posts
This is part of a list of blog posts.
To browse the contents go to

Other Caching Mechanisms


Web server caching

Most Web servers can cache static files. IBM HTTP Server, which comes with WebSphere Application Server, has a facility called the Fast Response Cache Accelerator (FRCA) on AIX and Adaptive Fast-Path Architecture (AFPA) on Windows that can cache static content (both platforms) as well as dynamic content (Windows only). This is a kernel-based cache and, by far, the fastest cache of any discussed here.


Edge Side Include (Web server plugin caching)

The ESI processor can cache pages or fragments of pages at the HTTP server layer.

Each time a new request is received by the plug-in, the ESI processor checks for it in the cache. If some fragments are already in cache, the plug-in can use it. If not, the ESI processor adds a specific header named Surrogate-Capabilities before forwarding the request to the application server.

The application server responds to the request. If servlet caching is enabled in the application server and the output is edge cacheable, the application server adds a Surrogate-Capabilities header with caching information. The plug-in stores, in the cache, the application responses, builds the page with all the nested components, and returns the answer to the client.


Hardware caching

Multiple network equipment providers offer hardware cache devices. These devices serve the same purpose as software caches do, namely to offload content. The main difference is that these appliances are not running full versions of an operating system. Instead, they use a specialized operating system that is dedicated to performing the caching function.

This operating system can include custom file systems (that offer higher performance than the operating system file system) and a significantly reduced instruction set. By placing dedicated appliances instead of software caching in your architecture, you can reduce total cost of ownership, because these appliances do not have to be managed as strictly as machines with full operating systems.

cachespec.xml explained

Cache entries are specified in the cachespec.xml file. This is a file that holds cache specification and invalidation policies. It can be placed in the /properties directory for global cache specifications or within WEB-INF directory of each Web module or META-INF of the enterprise bean directory.

cachespec.xml is used for Servlet/JSP caching, Webservice caching and Command caching.

When using the Distributed Object Map, cache is configured programmatically.

<cache> The root element of the cachespec.xml file,
appearing only once. It holds multiple <cache-entry>
stanzas.
<cache-entry> There is one of these for each item to be cached. Cache
entries can describe items to be cached, items which will
invalidate other cache entries, and dependencies between cache
entries. We will see examples of each below.
<class> Identifies the type of entry. Possible values are command,
servlet, and webservice.
<name> The name of the item to be cached. For commands, it should
be the fully qualified package and class name, including the .class
suffix. For servlets or JSPs, it should be the URI path relative to
the application's context root. For example, if the full URL to the
JSP is http://www.myco.com/myapp/products/catalogList.JSP,
the value here would be /products/catalogList.JSP. If
you were using the cachespec.xml file from the
application server properties directory (global), the entire URL
would be necessary. If you have multiple servlet aliases, multiple
<name> stanzas could be included here to list
each alias that you intend to cache.
<property> Used to set optional properties on a cache entry, such as
whether it can be cached outside of WebSphere Application Server,
and whether this entry can be persisted to disk. (There is a list
of definable properties in the WebSphere Application Server V5
Information Center.) There can be multiple properties per cache
entry.
<sharing-policy> Determines whether a cache entry should be shared between
distributed caches, and if so, how that should occur. Distributed
caches will be discussed later.
<cache-id> Where the cache identifiers for each cache entry are
configured. This is a key, similar to a database key structure that
identifies a particular cache entry as unique. Caching is generally
not as simple as specifying a particular servlet, JSP, or command
to cache. These must be qualified with attributes that distinguish
one cache entry from another. For example, caching a servlet that
returns a weather forecast by the URI /weather/forecast
would not be desirable; think of what would happen if one user
asked for the forecast for Miami and that result was cached, and
the following user invoked this URI to get the forecast for
Anchorage. In this case, there is probably a request parameter that
is sent along with the servlet invocation that has the city name or
zip code for the forecast. This is the piece of data that would
ensure the cached results are unique and match the request, so, for
example, Miami forecasts are always returned to other users asking
for Miami weather. This parameter should be designated as required,
as the cache entry is meaningless without it. Sample 5, below,
shows a similar cache specification. There may also be other
parameters as part of the cache ID, such as whether the user is
asking for a short-range or long-range forecast. Each component of
the cache ID is specified in a <component> tag,
as shown in the examples below.

Cache Invalidation


A cache can be invalidated using either of the 3 methods:

Simple timeout
On the cache specification for a particular cache entry, you can specify a default timeout value. This could be short term (seconds) or long term (years). It could also specify infinity.

Dynamic invalidation
Based on a logic we invalidate the cache during runtime. This logic could be specified through configuration or accessed through programmatic API.

Cache eviction
Cache entries are invalidated when the cache is full and they are purged to make way for new entries. For a static cache, this is simply which entry is closest to timing out. For a dynamic cache, a Least Recently Used algorithm is executed to determine which entries should be purged, as well as the priority assigned to each cache entry.

Webservice response caching in websphere

This cache holds the result of Web services SOAP invocations. Web services SOAP calls can be expensive, primarily due to the extensive parsing of XML files that must happen on both ends. Cache identifiers can be built from both HTTP headers and the SOAP envelope; in fact the entire SOAP envelope could be hashed and used as a cache ID.

A Tutorial here:
http://wasdynacache.blogspot.in/2010/02/caching-webservices-responses-in.html

Sample cachespec.xml in the case of a webservice caching,

<cache>
   <cache-entry>
      <class>webservice</class>
      <name>/soap/servlet/soaprouter</name>
      <cache-id>
         <component id="" type=SOAPAction>
            <value>urn:stockquote-lookup</value>
         </component>
         <component id="Hash" type="SOAPEnvelope"/>
         <timeout>600</timeout>
         <priority>1<priority>
      </cache-id>
      <cache-id>
         <component id="" type="serviceOperation">
            <value>urn:stockquote:getQuote</value>
         </component>
         <component id="Hash" type="SOAPEnvelope"/>
         <timeout>600</timeout>
         <priority>1</priority>
      </cache-id>
   </cache-entry>
</cache>

Above sample shows a simple cachespec.xml file with a single cache entry for a Web service call.

The class designation is obvious, and the name is the URI path to the soaprouter servlet, familiar to those who work with Web services. There are two cache IDs configured for this entry. The first is a combination of a stock quote lookup request and the SOAP envelope, and the second is a combination of a getQuote service operation and a hash of the SOAP envelope. With two cache IDs, the caching engine will parse from the top down and use the first one that fits the particular entry that it is working with at run time for either inserting a new entry into cache or returning a cache entry for a request.
Now let us look at a more complex example of a cachespec.xml file below.


<cache>
   <cache-entry>
 <class>servlet</class>
 <name>/ProductControllerServlet</name>
      <cache-id>
         <component id="action" type="parameter">
     <value>view</value>
            <required>true</required>
         </component>
         <component id="productID" type="parameter">
            <required>true</required>
         </component>
         <priority>3</priority>
         <timeout>20</timeout>
      </cache-id>
      <cache-id>
         <component id="action" type="parameter">
     <value>view</value>
            <required>true</required>
         </component>
         <component id="category" type="parameter">
            <required>true</required>
         </component>
         <priority>3</priority>
         <timeout>20</timeout>
      </cache-id>
      <dependency-id>category
         <component id="category" type="parameter">
            <required>true</required>
         </component>
      </dependency-id>
      <invalidation>category
         <component id="action" type="parameter" ignore-value="true">
     <value>update</value>
     <required>true</required>
  </component>
  <component id="category" type="parameter">
     <required>true</required>
         </component>
       </invalidation>
   </cache-entry>
</cache>

The above xml defines a single cache entry for a controller servlet, such as you might find in a MVC-type application.
From the top down, you see:
A cache ID for when the product controller servlet is run with a parameter named "action" with a value of "view", using the product ID. This probably indicates a view for a single product's information. Notice that not only is the parameter name of "action" provided as part of the cache ID, but a required value of "view" for that parameter is provided as well.
A cache ID for when the product controller servlet is run with parameter "action" with value "view" and a category parameter is provided. This would be an example of when a page of products in a certain category are requested.
A dependency ID named "category" and using the parameter name "category" and its value as the cache ID. This and other cache entries can have the same dependency ID and will be invalidated as a group if certain events occur (such as an invalidation rule firing).
An invalidation rule named "category", which causes it to be linked to the category dependency ID. It requires that the "action" parameter of this servlet have the value "update". Based on the linkage, when the servlet is run in update mode for this category, all cache entries for both the individual product view and the list for that category will be invalidated.

Servlets and JSP caching in Websphere


The servlet/JSP caching facility will catch the response from servlet/JSP invocation and cache the HTML results. Servlets are configured for caching via entries in the cachespec.xml file (described here). They can be designated by their URI path, or by classname. The latter option is more inclusive, since it will catch any invocation of the servlet regardless of the aliases. In most cases, the cache entry for the servlet needs to be further qualified by additional inputs, such as the request parameters or values from the user session information. We will explain this further in our section on specifying cache entries.

When preparing to execute a servlet, the dynacache engine filters the service() method of each servlet that is about to be executed, and determines if this matches any cache ID entries based on the parameters present. If a match is found, the cached results are returned rather than executing the service() method and all of the work that would have been done beyond it. This avoids all of the processing that would have been done by the servlet, resulting in a substantial performance boost. If there is not a cache entry for this ID, the service() method is executed as normal and the results are caught and placed in the cache before they are returned to the user.


Sample cachespec.xml file for a JSP

<cache>
   <cache-entry>
      <class>servlet</class>              
      <name>/displayForecast.jsp</name>
      <property name="EdgeCacheable">true</property>
      <cache-id>
         <component id="zip" type="parameter">
            <required>true</required>
         </component>
         <priority>3</priority>
         <timeout>20</timeout>
      </cache-id>
   </cache-entry>
</cache>

Above is a simple cachespec.xml file with a single cache entry for a JSP. You might notice that the class is servlet, while the name clearly identifies a JSP. This is because JSPs are compiled into servlets on the backend, so from the application server's perspective, they are essentially the same thing. This JSP has a property that states that it is "edge cacheable".

Notice that the cache ID is composed of a single request parameter, the zip code for the forecast. This means that if seven users request forecasts for seven different zip codes, there will be seven cache entries in the cache with the zip code serving as their key. Subsequent users requesting forecasts for any of these seven zip codes will be served up the forecast for the appropriate area. This cache entry has been configured with a priority of 3, meaning it gets three "free passes" if it is designated as ready to be invalidated by the LRU algorithm, mentioned earlier in our discussion on invalidation. The entry is set to a default timeout of 20 seconds; however, it could be invalidated earlier by means of a program API, another cache entry that is designated to invalidate it, or purging due to a full cache.

Websphere caching

Websphere provides caching for,

  1. objects (using DistributedMap)
  2. servlets and jsp
  3. web service caching
  4. objects (using the command programming model)
  5. static

Different ways to Invalidate cache

cachespec.xml Explained

Other caching mechanisms


External resources and references used:

http://www.ibm.com/developerworks/websphere/downloads/cache_monitor.html (enhanced cache monitor)
http://wpcertification.blogspot.in/2011/02/how-to-use-different-cache-instance-for.html
wpcertification.blogspot.in/2011/01/storing-custom-java-objects-in.html
http://wpcertification.blogspot.in/2011/01/information-about-dynacache.html
http://wpcertification.blogspot.in/2011/01/information-about-dynacache.html

http://pic.dhe.ibm.com/infocenter/wasinfo/v7r0/index.jsp?topic=%2Fcom.ibm.websphere.nd.doc%2Finfo%2Fae%2Fae%2Frdyn_cachespec.html

http://www.ibm.com/developerworks/websphere/techjournal/0405_hines/0405_hines.html


Command caching in Websphere (cluster)


The command cache can cache the results of the invocation of server-side commands, which implement the WebSphere Command Pattern interfaces. This is useful for caching intensive operations, such as complex SQL joins or host requests. The results here are typically an object or container of objects, rather than the HTML results that are held in the servlet/JSP cache.

The command pattern is widely used and relatively simple to retrofit to an existing application, should you feel it could benefit from this type of caching. Retrofitting involves writing thin command wrappers around the existing code. Similar to how the caching engine filters on a servlet or JSP's service() method, the command cache filters on a command's execute() method to determine if the command is cacheable and if there are already results in the cache.
The following steps are same irrespective of whether it's a standalone server or cluster.

Enable Bean caching



Install cacheMonitor.ear.

Now write a Bean class,

package beans;

import com.ibm.websphere.command.CacheableCommandImpl;

public class MyBean extends CacheableCommandImpl{

 private static final long serialVersionUID = 1L;
 
 private String beanId;
 private String response;
 
 public MyBean(String beanId){
  this.beanId = beanId;
 }
 
 @Override
 public boolean isReadyToCallExecute() {
  System.out.println("isReadyToCallExecute");
  return true;
 }

 @Override
 public void performExecute() throws Exception {
  System.out.println("performExecute");
  //do business logic here
  if(Integer.parseInt(beanId)>100){
   setResponse("hello...");
  }else {
   setResponse("...bye");
  }
   
 }

 public void setBeanId(String beanId) {
  this.beanId = beanId;
 }

 public String getBeanId() {
  return beanId;
 }
 public void setResponse(String response) {
  this.response = response;
 }

 public String getResponse() {
  return response;
 }

 @Override
 public String toString() {
  return "MyBean [beanId=" + beanId + ", response=" + response + "]";
 }
}


Now write the servlet class,

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import beans.MyBean;

import com.ibm.websphere.command.CommandException;

/**
 * Servlet implementation class CmdCacheServlet
 */
public class CmdCacheServlet extends HttpServlet {
 private static final long serialVersionUID = 1L;

 public CmdCacheServlet() {
  super();
 }

 protected void doGet(HttpServletRequest request,
   HttpServletResponse response) throws ServletException, IOException {
  response.setContentType("text/html");
  PrintWriter writer = response.getWriter();
  String id = request.getParameter("id");
  MyBean bean = new MyBean(id);
  try {
   System.out.println("before execute");
   bean.execute();
   System.out.println("after execute");
   writer.println("MyBean says: " + bean.getResponse());
   writer.println("Local port:" + request.getLocalPort());
   String memberName = (String) getServletContext().getAttribute(
     "com.ibm.websphere.servlet.application.host");
   writer.println("Cluster member:" + memberName);

   writer.flush();
   writer.close();
  } catch (CommandException e) {
   e.printStackTrace();
  }
 }

 protected void doPost(HttpServletRequest request,
   HttpServletResponse response) throws ServletException, IOException {
  // TODO Auto-generated method stub
 }

}

WEB-INF/cachespec.xml


<?xml version="1.0" ?>
<!DOCTYPE cache SYSTEM "cachespec.dtd">
<cache>
 <cache-entry>
  <class> command</class>
  <sharing-policy>not-shared</sharing-policy>
  <name>beans.MyBean.class</name>
  <cache-id>
   <component type="method" id="getBeanId">
    <required>true</required>
   </component>
   <priority>1</priority>
   <timeout>60</timeout>
  </cache-id>
 </cache-entry>
</cache>

Check the URL,
http://localhost/TestSR/CmdCacheServlet?id=150



[4/2/14 15:44:02:851 IST] 0000003b SystemOut O before execute
[4/2/14 15:44:02:852 IST] 0000003b SystemOut O isReadyToCallExecute
[4/2/14 15:44:02:853 IST] 0000003b SystemOut O performExecute
[4/2/14 15:44:02:853 IST] 0000003b SystemOut O after execute
[4/2/14 15:44:30:276 IST] 0000003b SystemOut O before execute
[4/2/14 15:44:30:277 IST] 0000003b SystemOut O isReadyToCallExecute
[4/2/14 15:44:30:278 IST] 0000003b SystemOut O after execute



Restart cluster.


<?xml version="1.0" ?>
<!DOCTYPE cache SYSTEM "cachespec.dtd">
<cache>
 <cache-instance name="services/cache/sercache">
  <cache-entry>
   <class> command</class>
   <sharing-policy>not-shared</sharing-policy>
   <name>beans.MyBean.class</name>
   <cache-id>
    <component type="method" id="getBeanId">
     <required>true</required>
    </component>
    <priority>1</priority>
    <timeout>60</timeout>
   </cache-id>
  </cache-entry>
 </cache-instance>
</cache>


Check URL,
http://localhost/TestSR/CmdCacheServlet?id=150




Object caching and replication in Websphere (cluster)


This cache holds Java objects for use in a distributed environment. For example, objects may be stored by one application server and then retrieved by other application servers in the same Data Replication Service (DRS) cluster. However, this cache is only available in the Enterprise edition of WebSphere Application Server.

These cache instances are retrieved by a JNDI name that is configured on the Cache Instance resource (which is similar to a JDBC resource). This cache can even be configured so that objects are persistent, flushed to disk when the server is stopped, and loaded again upon restart. Individual entries can be designated as non-shared, push (sent to all servers when they are cached), or pull (only their names are sent, and values are retrieved only when "pulled" from other servers)

The following steps are mostly the same whether you are trying on a standalone server or cluster.

Create a replication domain if not already created. It will automatically be created if “memory-to-memory” session replication is selected during cluster creation process.


Check if all the members are added to the domain.


Create object cache instance.
In admin console,
Resources -> Cache Instances -> Object Cache Instances






You might find a default WebSphere Dynamic Cache instance created that is bound into the global JNDI namespace with the name "services/cache/distributedmap". You could either use this default instance or create one.


Additional cache instances can also be created using a properties file cacheinstances.properties with the following format:
cache.instance.0=/services/cache/instance_one
cache.instance.0.cacheSize=1000
cache.instance.0.enableDiskOffload=true
cache.instance.0.diskOffloadLocation=${WAS_INSTALL_ROOT}/temp
cache.instance.1=/services/cache/instance_two
cache.instance.1.cacheSize=1500
cache.instance.1.enableDiskOffload=true
cache.instance.1.diskOffloadLocation=C:/disk

The distributedmap.properties file must be located in either you application server or application's classpath.

Add the cache service to individual MEMBERS.



Do the same for all members in the cluster.
Do a cluster restart.


Test using this servlet

import java.io.IOException;
import java.io.PrintWriter;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.ibm.websphere.cache.DistributedMap;

/**
 * Servlet implementation class ObjCacheServlet
 */
public class ObjCacheServlet extends HttpServlet {
 private static final long serialVersionUID = 1L;


 public ObjCacheServlet() {
  super();
 }

 protected void doGet(HttpServletRequest request,
   HttpServletResponse response) throws ServletException, IOException {
  String stringObj = (String) request.getParameter("obj");
  String stringKey = (String) request.getParameter("key");
  String invalidate = (String) request.getParameter("invalid");
  PrintWriter writer = response.getWriter();

  if (stringObj != null && stringKey != null) {
   putCache(stringKey, stringObj);
   writer.println("Storing object:" + stringObj + " with key:"
     + stringKey);
  } 
  
  if(invalidate !=null &&  stringKey != null){
   invalidateCache(stringKey);
   writer.println("Invalidated object with key:"
     + stringKey);
  }

  if(stringObj == null && stringKey != null){
   writer.println("Retrieved object:" + getCache(stringKey) + " for key:"
     + stringKey);
  } 
  
  
  writer.println("Local port:" + request.getLocalPort());
  String memberName = (String) getServletContext().getAttribute(
    "com.ibm.websphere.servlet.application.host");
  writer.println("Cluster member:" + memberName);

  writer.flush();
  writer.close();
 }

 protected void doPost(HttpServletRequest request,
   HttpServletResponse response) throws ServletException, IOException {
  // TODO Auto-generated method stub
 }

 private static DistributedMap getDm() throws Exception {
  Context ctx;
  DistributedMap dm = null;

  try {
   ctx = new InitialContext();
   dm = (DistributedMap) ctx.lookup("services/cache/objcache");
   if (dm == null) {
    System.out.println("dm is null");
   }
  } catch (NamingException e) {
   e.printStackTrace(System.out);
  }

  return dm;
 }

 public static void putCache(Object key, Object obj) {
  System.out.println("putCache");
  if ((obj == null) || (key == null)) {
   // log error
   return;
  }

  try {
   String keyAsString = ((String) key).toLowerCase();

   getDm().put(keyAsString, obj);
   System.out.println("Storing obj with key:" + key);
  } catch (Exception e) {
   e.printStackTrace(System.out);
  }
 }

 public static Object getCache(Object key) {
  System.out.println("getCache");
  Object obj = null;
  try {
   String keyAsString = ((String) key).toLowerCase();
   obj = getDm().get(keyAsString);
  } catch (Exception e) {
   e.printStackTrace(System.out);
   obj = null;
  }

  return obj;
 }

 public static void invalidateCache(Object key) {
  System.out.println("invalidateCache");
  try {
   getDm().invalidate(key);

  } catch (Exception e) {
   e.printStackTrace(System.out);
  }
 }
}

Now hit the urls and try different cases.

http://localhost/TestSR/ObjCacheServlet?obj=firstobj&key=1

Storing object:firstobj with key:1
Local port:9088
Cluster member:MEMBER_002

http://localhost/TestSR/ObjCacheServlet?key=1

Retrieved object:firstobj for key:1
Local port:9091
Cluster member:MEMBER_003

http://localhost/TestSR/ObjCacheServlet?key=1

Retrieved object:firstobj for key:1
Local port:9087
Cluster member:MEMBER_001

http://localhost/TestSR/ObjCacheServlet?key=1&invalid=true

Invalidated object with key:1
Retrieved object:null for key:1
Local port:9088
Cluster member:MEMBER_002

http://localhost/TestSR/ObjCacheServlet?key=1

Retrieved object:null for key:1
Local port:9091
Cluster member:MEMBER_003