简介: 缓存是目前大型复杂应用特别是互联网应用中提升性能和并发能力的关键技术之一。动态高速缓存服务(DynaCache)是 IBM™ WebSphere® Application Server(后面简称 WAS)提供的扩展服务之一。该服务提供极大的灵活性,开发人员可以根据不同的场景选择不同的编程模式来满足开发的需要。恰当的使用该服务可以极大的提升运行在 WAS 平台上的应用系统的的响应能力和吞吐能力,在同等的硬件条件下提供更强大的处理能力,满足企业日益增长的业务需要。
通过本教程,您可以了解如何使用 WAS 的动态高速缓存服务缓存 Servlet 对象,JSP 页面,WebSphere Command 对象;如何在 cachespec.xml 文件中进行配置或者编写唯一标识的生成类实现根据业务需要缓存同一个对象的不同版本。本教程也涉及直接操作动态缓存和使用 Cache Monitor 监控和管理动态高速缓存。
完成本教程后,您应该了解如何:
- 配置 cachespec.xml 缓存 Servlet 或 JSP 页面
- 在 cachespec.xml 中配置缓存规则缓存同一个 Servlet 对象或 JSP 页面的不同版本
- 编写 WebSphere Command 对象
- 编写生成缓存唯一标识的 Java Class
- 直接操作动态高速缓存
- 安装并使用 Cache Monitor
要成功使用本教程,您应熟悉 Java 开发的一般知识,特别是具备服务器端 Java 开发的基础。您应了解 WAS 和 Rational® Application Developer for WebSphere® Software (下面简称 RAD) 的一般概念,并熟悉部署描述符和 WAR 存档等基本的 J2EE 概念,对 WAS 的配置和管理有所了解。
您需要下载下列必需的(免费)应用程序,才能使用本教程及其中的示例代码:
- IBM™ Rational® Application Developer for WebSphere® Software
- IBM™ WebSphere® Application Server 6.1 或者以上版本
如果读者无法获得 RAD 和 WAS 的正式版本,可以考虑下载和使用 RAD 的试用版 和 WebSphere® Application Server for Developers。如果读者不想下载 RAD 的试用版,作者也提供了可供直接编译的源代码包 DynaCacheTutorial.zip,下载并将其解压缩到某个目录,修改 was.properties 文件中 was.app.dir 的值为的 WAS 的目录,然后运行“ant war”进行编译。使用这种方法用户可以用您所喜欢的编辑器直接修改相应的代码并进行编译安装。
硬件配置应包括:支持上面列出的软件,并且至少具有 2GB 内存(推荐为 3GB)。本教程中的说明基于 Microsoft® Windows® 操作系统,但也适用于 Linux® 和 UNIX® 操作系统。
高速动态缓存做为 WAS 的一个扩展服务从 5.0.2 开始就被包含在从 WAS Express 开始的各个版本。该服务可以缓存 WebSphere Command 对象、Servlet 和 JavaServer Pages(JSP)的输出,从而明显提升应用程序性能。动态高速缓存服务位于应用程序服务器 Java 虚拟机(JVM)内部,通过拦截对可高速缓存对象的调用隐式的实现了对缓存的调用,程序员甚至意识不到它的存在。图 1 展示了缓存命中和不命中的两种情况下系统的流程,如果缓存命中将避免执行后面复杂的商业逻辑,业务逻辑的执行时间大大缩短了。
图 1. 缓存命中和失败
默认情况下 WAS 是开启动态高速缓存服务的,用户可以按照以下的步骤检查该服务是否开启并开启 Servlet 缓存
- 登录 WAS 的管理控制台,如 http://localhost:9060/ibm/console
- 输入管理员的用户名和密码
- 单击 Servers > WebSphere application servers > server1,在 Configuration 选项卡下面选择 Container Services > Dynamic cache service,如 图 2
- 确保在 图 3“Enable service at server startup”被选中(在 WAS 7 则没有这个选项,用户是不能够关闭动态高速缓存服务的)
图 2. 打开动态高速缓存服务
图 3. 确认动态高速缓存已被开启
然后确认 Servlet 的缓存被开启了,默认情况下 WAS 是不开启该选项的,依照如下的步骤启动该功能
- 单击 Servers > WebSphere application servers > server1,在 Configuration 选项卡下面选择 Web Container Settings > Web container
- 在 图 4 选中“Enable Servlet caching”,然后选择“OK”按钮
- 在接下来的界面选择“Save”连接,保存修改
图 4. 开启 Servlet 缓存
在本文中我们会创建一个简单的 Web 应用,对显示的时间进行缓存。该例子会有 Servlet,JSP,WebSphere Command 等多个版本
按照以下步骤创建一个 WEB 工程
- 启动 Rational Application Developer for WebSphere
- 创建一个 Dynamic Web Project,以 DynaCacheTutorial 为工程的名字,将 Dynamic Web Module version 选择为 2.4,这样编译的 WAR 就可以在 WAS 6.1 中部署了
- 同时创建一个名为 DynaCacheTutorialEAR 的 EAR 工程,将 EAR version 设为 1.4,同样这样确保可以部署到 WAS 6.1 上面
- 将 DynaCacheTutorial 加到 DynaCacheTutorialEAR 工程中去
- 将 DynaCacheTutorialEAR 部署到 WAS 上确保运行没有错误
在本节开始以前请确保您按照 在 WAS 中开启 Servlet cache 一节的方法启用了 Servlet 缓存。下面从一个简单的 Servlet 例子开始介绍如何使用动态高速缓存,依照下面的的步骤创建一个 Servlet。
- 选择 File > Other > Web > servlet
- 在接下来的界面 Java package 填为 demo,Class name 填为 UnCachedServlet,选 Next 进入下一个界面
- 在接下来的界面接受缺省的设置(Name:UnCachedServlet,URL Mappings:/UnCachedServlet),选 Next 进入下一个界面
- 在接下来的界面中取消 doPost() 选中,选 Finish 结束设置
- 将 doGet() 方法替换如 清单 1 所示
清单 1. 不被缓存的 Servlet - UnCachedServlet.java
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter ut = response.getWriter(); out.println(""-//W3C//DTD HTML 4.01 Transitional//EN\">"); out.println(""); out.println(" A Servlet"); out.println(" "); out.println(" The Current time is " + new java.util.Date() + " |
清单 1 是一个非常简单的在页面上显示当前时间的 Servlet,在 WAS 部署成功以后,在浏览器的地址栏中输入如下的地址 http://localhost:9080/DynaCacheTutorial/UnCachedServlet,输出可能如 图 5(当然您得到的是当前的时间)
图 5. 未被缓存的 Servlet 的输出
每当您刷新这个页面的时候,输出的时间应该如预期的总是返回当前时间。
接着创建另一个 Servlet(demo.CachedServlet,Servlet 名字为 CachedServlet,映射为 /CachedServlet),并尝试缓存其内容,将其 doGet() 方法替换如 清单 2
清单 2. 被缓存的 Servlet - CachedServlet.java
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter ut = response.getWriter(); out.println(""-//W3C//DTD HTML 4.01 Transitional//EN\">"); out.println(""); out.println(" A Servlet"); out.println(" "); out.println(" The time will not change unless time is out or cache is cleared "); out.println(" The time is " + new java.util.Date() + ""); out.println(" "); out.println(""); out.flush(); out.close(); System.out.println(this.getClass().getName() + ".doGet() run!!!"); } |
在 WAS 上面部署后在浏览器的地址栏中输入如下的地址 http://localhost:9080/DynaCacheTutorial/CachedServlet,输出可能如 图 6
图 6. 被缓存的 Servlet 的输出
但是当再次刷新的时候,时间仍然是马上发生变化的,缓存显然并没有起作用。缓存不是无缘无故的就发生了的,是需要进行配置。下面做最重要的步骤,在 WebContent/WEB-INF 下创建一个名为 cachespec.xml 的文件,其内容如 清单 3
清单 3. 在 cachespec.xml 指定 Servlet 的缓存策略
<?xml version="1.0"?> servlet/CachedServletfalsefalse0300 |
手工重新部署或者等待 RAD 自动重新部署以后再次刷新 http://localhost:9080/DynaCacheTutorial/CachedServlet,打印出来的时间竟然神奇般的凝固不变了。但超过五分钟以后刷新时间就会发生变化。就这样通过创建了一个 XML 文件并在其中指定需要缓存的 Servlet 的名字和时间就完成了 Servlet 的缓存了,而代码本身没有任何的改变。这就是动态高速缓存强大的地方,它尽可能的采取了非侵入的方法不会污染已有的代码了。
Servlet 现在已经很少被用来产生页面,下面介绍如何在动态高速缓存中缓存 JSP 页面。依下面的步骤创建一个 JSP 页面
- 选择 File > Others
- 在文件类型中选择 Web Page,选择 Next 进入下一个界面
- 选择 JSP,文件名为 cached.jsp,选择 Finish 完成设置
- 将 JSP 页面的主体部分的替换如 清单 4 所示
- 然后在 cachespec.xml 中添加如 清单 5 所示的内容
清单 4. 被缓存的 JSP
The time will not change unless time is out or it's cleared The time is: |
清单 5. 在 cachespec.xml 缓存 JSP
servletcached.jspfalsefalse0300 |
通过浏览器访问 http://localhost:9080/DynaCacheTutorial/cached.jsp,得到的结果和上一章 Servlet 一样的缓存效果效果。在 WAS 中缓存 JSP 就是如此的简单。
前面无论是 Servlet 还是 JSP 总是输出同样的结果,实际应用中这种情况很少出现;往往是根据不同的输入参数,输出不同的结果。下面就模拟这样场景,根据不同的用户缓存不同的时间。首先在 WebContent 目录下创建两个 parameter.jsp 和 embed.jsp 两个 JSP 文件,内容分别如 清单 6 和 清单 7,然后在 cachespec.xml 中添加如 清单 8 的内容。请注意缓存的是 embed.jsp 而不是 parameter.jsp。
清单 6. parameter.jsp
parameter.jsp Input Your Name: The current time is: |
清单 7. embed.jsp
清单 8. embed.jsp 的 JSP 的缓存策略
servletembed.jspfalsefalsefalse0300 |
在浏览器中输入 http://localhost:9080/DynaCacheTutorial/parameter.jsp,分别输入 WAS 和 Rational,点击“Submit”按钮,您会发现 WAS 和 Rational 缓存的时间各不相同,且相互不影响。先观察一下 Cache Monitor 的输出(Cache Monitor 的安装和使用可以参见 Cache Monitor 一章),该输出类似于 图 7,明显 embed.jsp 有两个不同的页面缓存。观察其唯一标识(Cache ID)的差别就是 name=?。动态高速缓存是如何做到这个的?关键在于 cachespec.xml 中缓存策略使用了 request 中的名为 name 的参数做为缓存唯一标识。从这个例子很容易将其扩展到如下两种应用环境:
- 实现一个页面根据不同的输入参数缓存不同的结果
- 只缓存页面的一个部分,对于 Master-Detail 页面结构的设计非常有用
图 7. 同一个页面的多个缓存版本
读者可以把动态高速缓存看成一个复杂的 hashtable,可以通过唯一标识定位到缓存的一个对象。
有时候要缓存不仅仅是 Servlet 和 JSP 页面,用户还想缓存一个 Java 类的调用结果。WAS 提供了一种 command 的模式帮助用户缓存 Java 对象。使用该模式前用户必须提供所有的前置条件,然后运行 execute() 方法,该方法会处理缓存的命中,最后获取所需的数据。如 图 8 所示,要取得股票的价格,用户必须先调用 setSymbol() 提供股票代码,然后运行取价格的逻辑 execute(),最后调用 getPrice() 获得价格。下面是编写 command 对象的一般步骤
- 定义一个 Java 类扩展 com.ibm.websphere.command.CacheableCommandImpl 类并实现相应的接口
- 编写 setXXX() 方法用于设置前置条件
- 重载 isReadyToCallExecute() 方法检查前置条件是否完备
- 重载 performExecute() 方法执行真正的逻辑
- 选择是否重载 setOutputProperties() 方法只从动态高速缓存中取回部分属性
- 编写 getXXX() 方法返回所需要的结果
图 8. command 模式
下面用 command 模式实现前面 parameter.jsp 的功能。首先定义如 清单 9 的接口,实现类类似于 清单 9
清单 9. command 的接口
package demo; import java.util.Date; public interface DateService { String getUser(); Date getCached(); Date getUnCached(); } |
清单 10. DateService 的实现
package demo; import java.util.Date; import com.ibm.websphere.command.CacheableCommandImpl; import com.ibm.websphere.command.TargetableCommand; public class DateServiceCommand2 extends CacheableCommandImpl implements DateService{ private static final long serialVersionUID = 1L; @Override public boolean isReadyToCallExecute() { return user != null; } @Override public void performExecute() throws Exception { // Do nothing here } // Try to comment this function to see the result @Override public void setOutputProperties(TargetableCommand paramTargetableCommand) { DateServiceCommand2 command = (DateServiceCommand2) paramTargetableCommand; cached = command.cached; } @Override public String toString() { return String.format("", user, cached, unCached); } public Date getCached() { return cached; } public Date getUnCached() { return unCached; } public String getUser() { return user; } public void setUser(String user) { this.user = user; } private String user = null; private Date cached = new Date(); private Date unCached = new Date(); } |
在 cachespec.xml 中添加如 清单 11 的策略用于缓存前面编写的 command 对象。
清单 11. 缓存 command 的策略
commanddemo.DateServiceCommand20300 |
编写如 清单 12 的 JSP 文件,调用该 command 对象
清单 12. 使用 command 的 JSP 页面
Input Your Name Current time is: |
有时候唯一标识是无法通过简单的配置产生,比如需要根据登录的用户所在的地区进行缓存。对于这种情况动态高速缓存提供了使用 Java 代码生成唯一标识的方法。用户需要一个 Java 类来实现 Servlet 的 com.ibm.websphere.servlet.cache.IdGenerator 接口,如果是 Web Service 则实现 com.ibm.websphere.webservices.IdGenerator 接口,command 类则是 com.ibm.websphere.command.CommandIdGenerator 接口。无论是那种方法用户都需要实现 getId() 这个方法,返回一个 String 值表示合法的标识,如果不希望被缓存则返回 null。清单 13 是 command3 类的标识生成类,清单 13 则是在 cachespec.xml 中使用该唯一标识生成类。
清单 13. command3 的 idgenerator 类
package demo; import java.util.ArrayList; import com.ibm.websphere.command.CacheableCommand; import com.ibm.websphere.command.CommandIdGenerator; public class DateServiceIdgenerator implements CommandIdGenerator { @SuppressWarnings("unchecked") public String getId(CacheableCommand arg0, ArrayList arg1) { DateService command = (DateService) arg0; if (command != null) return command.getUser(); else return null; } } |
清单 14 是 command3 缓存策略
清单 14. 用 idgenerator 缓存 command
commanddemo.DateServiceCommand3demo.DateServiceIdgenerator0300 |
更详细的内容可以参考附件中的 command3.jsp 。标识生成类同样也适用于 Servlet、JSP 等的缓存。
有时候用户不希望使用这么复杂的编程模型,而是希望直接将数据存入动态高速缓存。这时可以使用 com.ibm.websphere.cache.DistributedMap 接口来操作动态高速缓存中的一个实例,用 get() 方法获得缓存对象,用 put() 方法将对象放入缓存,用 invalidate() 方法清除缓存。
有两种方法获得 DistributedMap 的一个实例。
- 调用 com.ibm.websphere.cache.DynamicCacheAccessor 类的静态方法 getDistributedMap() 获得 Servlet 缓存实例,清单 15 演示了这种用法,但是该方法已经被声明为 Deprecated,不建议使用
- 如 清单 15 通过 JNDI 查询获得缓存实例
清单 15. directuse1.jsp 用静态方法获得缓存实例
清单 16. directuse2.jsp 通过 JNDI 查询获得缓存实例
直接操作动态高速缓存对象则不需要在 cachespec.xml 做任何配置。WAS 会重新载入 cachespec.xml,然后刷新相应的缓存策略,这样用户可以在系统运行的时候动态调整缓存策略。直接操作缓存不会利用该特性了,相对不是那么方便,建议尽可能不要直接操作动态高速缓存。
动态高速缓存只有一种失效模式:定时失效。这样就带来了一个问题,如果被缓存的数据时效要求很高,如股票的价格,那么如果缓存的价格和当前价格不一致就会造成巨大的损失,要是可以及时清除缓存就可以避免这类问题。清单 17 展示了如何清除一条缓存。清除缓存是比较容易的,关键是找到合适的清除缓存的时机,在缓存命中率和数据的时效性中得到一个最佳平衡。
清单 17. invalidate.jsp 演示如何清楚缓存
This page demo how to invlidate a cache item |
WAS 提供了一个非常强大工具 Cache Monitor 帮助诊断和分析动态高速缓存的使用。该工具安装包位于 AppServer 的 installableApps 目录下,文件名为 CacheMonitor.ear。依照下面的步骤进行安装
- 登录 WAS 的管理控制台,如 http://localhost:9060/ibm/console
- 输入 WAS 管理员的用户名和密码
- 选择 Applications > New Application > New Enterprise Application
- 在 Path to the new application 选择 Local file system,通过“Browse...”按钮定位到 CacheMonitor.ear 的路径
- 在下一个界面选择“Fast Path - Prompt only when additional information is required.”
- 在“Step 1: Select installation options”接受默认值,选择 Next 按钮进入下一步
- 在“Step 2: Map modules to servers”选择 server1,选择 Next 按钮进入下一步
- 在“Step 3: Summary”选择 Finish 按钮结束安装
- 在接下来的界面选择 Save 连接保存设置
默认安装的 Cache Monitor 提供的功能比较有限,读者通过下载 Extend Cache Monitor 扩展其功能,具体步骤如下
- 下载 Extend Cache Monitor,WAS 7.0、6.1、6.0 对应的版本各不相同,请根据您的 WAS 的版本选择
- 登录 WAS 的管理控制台,如 http://localhost:9060/ibm/console
- 输入 WAS 管理员的用户名和密码
- 选择 Applications > Application Types > WebSphere enterprise applications
- 在如 图 9 所示的界面,选择 Dynamic Cache Monitor,点击 Update 按钮
- 根据下载站点上的提示进行安装
图 9. 安装 Extend Cache Monitor
现在还无法使用 Cache Monitor,还需要按照下面的步骤设置访问控制
- 在 图 9 中选择 Dynamic Cache Monitor,对其进行访问控制设置
- 在接下来的界面中选择 Detail Properties > Security role to user/group mapping,点击进入配置界面
- 选中 administrator,再选择“Map Special Subjects”
- 可以参考 图 10 选择 Everyone 有管理员权限,这样匿名用户也可以使用 Cache Monitor 了(在生产环境中不建议如此设置)
- 选择 OK 按钮推出,在接下来的界面选择 Save 保存设置
图 10. 设置访问控制
在使用 Cache Monitor 以前请确保该应用已经启动。在浏览器地址栏中输入 http://localhost:9080/cachemonitor,会出现如下 图 11 的界面
图 11. Cache Monitor
然后访问 http://localhost:9080/DynaCacheTutorial/CachedServlet 和 http://localhost:9080/DynaCacheTutorial/cached.jsp 两个连接。接着切换到 Cache Monitor 界面,看到和 图 12 类似的结果。点击“/DynaCacheTutorial/cached.jsp:requestType=GET”连接进入如 图 13 所示的界面,在这里我们看到了内容和 http://localhost:9080/DynaCacheTutorial/cached.jsp 是一致的,这个功能很有利于调试。另一个有用的调试功能是点击每一条被缓存内容前面的 Invalidate 按钮清除这条内容。
图 12. Cache Monitor 显示 Servlet 和 JSP 被缓存了
图 13. 被缓存的 JSP 的内容
Cache Monitor 还有一个重要的作用就是分析缓存的效率,和所有的缓存的一样我们期望缓存的命中率越接近 100% 越好,计算方法是用 图 12 中的“Cache Hits”减掉“Cache Misses”以后的值除以“Cache Hits”,如 图 14 中缓存命中率为 99%,一个非常理想的值。“Used Entries”表示当前缓存被使用的情况,当缓存被填满的时候,如果没有设置“Disk Offload”,那么低优先级级的缓存项就会被从动态高速缓存中剔除出去。
图 14. 缓存命中率的统计
在前面的例子中无论是 Servlet 还是 JSP 页面本身没有什么特殊的地方,关键在于 cachespec.xml 文件,它神奇的指定了缓存的策略。本章将重点讲解一下该文件核心部分的结构,该文件中每一条 cache-entry 均对应这一个 Servlet、JSP 或者 WebSphere command。下面介绍一下几个关键的域
- class 指定需要缓存的对象,合法的取值可以是 command,Servlet,webservice,JAXRPCClient,static,portlet
- name 指定被缓存的对象的名字,不同的对象取值类型是不同的,Servlet 和 JSP 取 URI,command 对象取类的全名,portlet 取 portlet 名字
- property,建议 store-cookies 和 save-attributes 为 false,这样可以减少由于序列化失败导致在集群中缓存复制失败的可能性
- cache-id 指定对象的不同调用生成唯一标识。这些标识是由用户编写的定制 Java 代码或每个高速缓存条目的高速缓存策略中定义的规则构建产生,用户可以为一个对象指定多个标识生成规则。如果高速缓存标识生成规则产生不了有效的标识,对象就不被缓存。每个 cache-id 元素都定义高速缓存对象的规则,并且它是由子元素组件(timeout、inactivity、priority、property、idgenerator 和 metadatagenerator)组成的。更多的细节参考信息中心。后面会重点介绍 idgenerator。
您可以选择直接部署编译好的 war 包,并在浏览器中输入 http://localhost:9080/DynaCacheTutorial,里面有到各个页面的快速连接,这样您就可以很容易体会一下动态高速缓存的强大。
通过本教程您学习了如何使用 WAS 的动态高速缓存,感受了其强大的功能和使用的便利。如果使用得当可以让应用系统的性能提升事半功倍。但是读者如需要知道更多的关于动态高速缓存的内容请参考后面提及的参考资料。
原文链接:http://www.ibm.com/developerworks/cn/websphere/library/techarticles/1008_shenhf_wascache/1008_shenhf_wascache.html
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/14789789/viewspace-671162/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/14789789/viewspace-671162/