【转】在 WebSphere Application Server V6 中配置和使用 Web Service 缓存

在WebSphere Application Server V6 中配置和使用Web Service缓存原文在这

1. 动态缓存服务介绍

 

在使用Web Service时候,如果充分合理使用缓存,可以减少程序调用的时间,减轻系统的负载,在一定程度上解决Web Service效率较低的问题。WAS V6提供了动态缓存服务,可以对servlets, JSPs, commands或者 Web Services进行缓存,在这里我们主要讨论Web Service 缓存,缓存可以存在Web Service客户端,也可以放在服务器端,分别称之为Web Service Client 缓存和Web Service Server 缓存。

图1 是 Web Service Server 缓存的原理图,部署在WAS中的Web Service在接受到Web Service 客户端的请求后,Cache Service首先根据请求消息产生一个Cache ID,根据这个ID查找Cache Pool:

  • 如果找不到相应的Cache Object,那么调用Web Service,将Cache ID和调用结果放入Cache Pool中,并构造响应消息返回,如图1中蓝线所示。
  • 如果找到Cache Object,那么直接根据Cache Object构造响应消息返回,如图1中红线所示。


图1 Web Service Server 缓存原理图
【转】在 WebSphere Application Server V6 中配置和使用 Web Service 缓存

图2 是 Web Service Client 缓存的原理图, Web Service Client 缓存的原理与Web Service Server 缓存类似,只不过缓存放在Client端。当Web Service Client调用远程Web Service得到结果时候,需要将Cache ID和结果对象缓存到本地Server;如果下次请求产生的Cache ID已经存在Cache中,直接根据这个ID取出结果对象,而不需要调用远程Web Service。


图2 Web Service Client 缓存原理图
【转】在 WebSphere Application Server V6 中配置和使用 Web Service 缓存

为了使用WAS的动态缓存功能,不需要改变Web Service实现的代码或者改变现有的配置,仅仅需要在开发完成Web Service后定义一个缓存规则文件cachespec.xml,该配置文件定义了需要缓存的对象,产生Cache ID的规则以及timeout失效时间等。同时WAS提供缓存的监控页面,允许用户查看缓存统计信息,清除缓存等。

 

 

2. 配置WAS支持Web Service 缓存

 

2.1.Enable Web Service缓存

启动WAS后,启动管理控制台,选择Servers → Application servers → server1 → Container services → Dynamic cache service,确保Enable service at server startup已经选中,如图3所示。


图3 选中启动动态缓存服务
【转】在 WebSphere Application Server V6 中配置和使用 Web Service 缓存

选择Servers → Application servers → server1 → Web Container Settings → Web container,选中Enable servlet caching。保存所做的修改,重启Server后,修改生效,如图4所示。


图4 选中Servlet缓存
【转】在 WebSphere Application Server V6 中配置和使用 Web Service 缓存

2.2.安装CacheMonitor

WAS V6提供了CacheMonitor 应用程序以监视Web Service 缓存,该应用程序放在WAS_HOME/installableApps/CacheMonitor.ear中,需要自己安装。启动管理控制台后,进行以下步骤:

1. 新建虚拟主机和别名:为了安全原因,WAS不允许CacheMonitor直接安装在缺省主机。可以新建一个虚拟主机,Environment → Virtual Hosts → New,指定名字cache_monitor。指定别名(Aliases) 属性,Host Name是*, Port是9070或者其它没有使用过的端口。

2. 新建一个Web Container Inbound Chain:选择Servers → Application servers → server1 → Web Container Settings → Web container transport chains,点击New新建一个Transport chain,名字是CacheMonitor,然后点击Next,Port Name和Port都输入9070,点击Finish完成。

3. 部署CacheMonitor:和普通的EAR一样安装CacheMonitor.ear,注意安装Web模块时候指定虚拟主机为上面定义的cache_monitor,如图5所示:


图5 设定自定义虚拟主机
【转】在 WebSphere Application Server V6 中配置和使用 Web Service 缓存

4. 部署完成后,重启WAS,在浏览器输入地址http://localhost:9070/cachemonitor,就可以看到缓存的监控页面了,如图6所示:


图6 CacheMonitor页面
【转】在 WebSphere Application Server V6 中配置和使用 Web Service 缓存

 

 

3. 示例

 

该例子是比较简单的Echo例子,提供echo方法将请求的消息直接返回去,该例子用来说明如何开发Web Service Service 缓存和Web Service Client 缓存。

如图7所示,EchoServerCacheWeb是一个基于动态Web Project的Web Service,仅提供一个echo方法,该Web Service使用Server端的缓存。EchoClientCacheWeb也是一个基于动态Web Project的Web Service,内部调用EchoServerCacheWeb的echo方法,该Web Service使用Client端的缓存。EchoCacheTest是一个普通的Java Project,用来测试上面的Web Service。下面让我们在RAD6.0 (Rational Application Developer V6)一步步的开发这个例子吧。


图7 例子示例图
【转】在 WebSphere Application Server V6 中配置和使用 Web Service 缓存

3.1. 开发Web Service 服务器端缓存例子

1. 新建一个动态Web Project,名字是EchoServerCacheWeb,在里面新建一个class,名字是com.ibm.cache.EchoService,该类仅有一个echo方法,将请求的String参数返回,为了测试效果该方法内部延时1秒,如清单1所示。完成以上操作后将该方法发布为Java Bean Web Service操作。


清单 1. echo方法代码

public String echo(String para)
{
 try {
  Thread.currentThread().sleep(1000);
 } catch (InterruptedException e) {
 }  
 System.out.println("[EchoService] called, echo : " + para);
 return para;
}

2. 编写cachespec.xml Cache规则文件,这是Web Service 缓存的配置文件,编写完成后将该文件放在WEB-INF目录下面,如果是EJB project,则需要将其放在META-INF下面。该规则文件如清单2所示:


清单2 cachespec.xml Cache规则文件

<?xml version="1.0" ?> 
 <!DOCTYPE cache SYSTEM "cachespec.dtd"> 
 <cache> 
  <cache-entry> 
   <class>webservice</class> 
   <name>services/EchoService</name> 
   <sharing-policy>not-shared</sharing-policy> 
   <cache-id>
    <component id="Hash" type="SOAPEnvelope"/> 
    <timeout>10</timeout> 
   </cache-id> 
  </cache-entry> 
 </cache>

其中class元素表示需要缓存的对象,元素值webservice表示这是Web Service Server端的缓存。

name元素表示一个cache entry的名字,对于Web Service Server 缓存来说,name元素值表示需要缓存的Web Service的URI,在这里是services/EchoService,运行时WAS会将context root添加到该值前面拼成完整的URL地址EchoServerCacheWeb/services/EchoService。

sharing-policy表示在一个cluster环境中如何共享缓存,对单个server来说其值只能是not-shared。

cache-id元素表示WAS如何为需要缓存的对象产生一个cache ID,每一个cache object都有一个cache ID与它唯一地对应。cache-id元素可以有多个,cache entry会依次执行每个cache-id规则,直至产生cache id。只有能够创建cache id的cache entry才能进行缓存。cache-id可以有以下子元素:

  • timeout表示保持cache的时间,这里表示10秒之后cache entry会被清空。
  • component来产生具体的cache id,component可以有多个,WAS会将所有的component产生的字符串拼接成cache id。在这里表示使用请求的SOAP消息来产生Hash值做为cache id,每一个不同的请求消息产生一个不同的cache id。
  • 可以自己编写java代码来产生cache id,需要在cache-id中添加idgenerator子元素指定产生cache id的类名。

如何产生cache id是我们主要考虑的部分,通过灵活地配置component元素,我们可以指定Web Service的某些方法可以产生cacheid,可以从soap头部的某些参数或者根据Web Service的操作名字或者参数等产生cache id等。具体规范可以参看WAS information center。 清单3规则表示只有getQuote操作(名称空间是urn:stockquote)才能进行缓存,该缓存使用soap体的Hash值做为cache id。


清单3只有getQuote操作才能进行缓存的规则文件

<cache-id>
 <component id="" type="serviceOperation">
  <value>urn:stockquote:getQuote</value>
 </component>
 <component id="Hash" type="SOAPEnvelope"/>
  <timeout>3600</timeout>   
 </component>
</cache-id>

3. 新建EchoCacheTest Project,增加一个类EchoTest,在该类增加方法测试EchoServerCacheWeb,该方法内部连续调用Echo Service 5 次,记录每次花费时间,代码如清单4。


清单4 测试EchoServerCacheWeb的方法

public void testServerCache()
{
 int total = 5;
 EchoServiceProxy proxy = new EchoServiceProxy();
 long startTest = System.currentTimeMillis();
 for (int i = 0; i < total; i++)
 {
  try {
   long startTime = System.currentTimeMillis();
   proxy.echo("hello");
   long endTime = System.currentTimeMillis();
   System.out.println("[EchoTest] the " + (i+1) 
        + " call spent time: " + (endTime - startTime));
  } catch (RemoteException e) {
   e.printStackTrace();
  }
 }
 long endTest = System.currentTimeMillis();
 System.out.println("[EchoTest] all test spent time: " + (endTest - startTest));
}

4. 运行EchoCacheTest project的testServerCache方法,查看测试结果:

在Client端控制台:
[EchoTest] the 1 call spent time: 1742
[EchoTest] the 2 call spent time: 30
[EchoTest] the 3 call spent time: 30
[EchoTest] the 4 call spent time: 20
[EchoTest] the 5 call spent time: 30
[EchoTest] all test spent time: 1852

Server端SystemOut.log文件:
SystemOut O [EchoService] called, echo : hello

如果取消EchoServerCacheWeb的缓存,删除cachespec.xml,重新测试结果如下

在Client端控制台:
[EchoTest] the 1 call spent time: 1773
[EchoTest] the 2 call spent time: 1031
[EchoTest] the 3 call spent time: 1022
[EchoTest] the 4 call spent time: 1021
[EchoTest] the 5 call spent time: 1042
[EchoTest] all test spent time: 5889
Server端SystemOut.log文件:
SystemOut O [EchoService] called, echo : hello
SystemOut O [EchoService] called, echo : hello
SystemOut O [EchoService] called, echo : hello
SystemOut O [EchoService] called, echo : hello
SystemOut O [EchoService] called, echo : hello
SystemOut O [EchoService] called, echo : hello

从两次测试的输出结果我们可以看到:如果没有缓存,客户端调用Web Service 5次,Server端就执行5 次,每次时间差不多。而使用了Web Service 服务器端缓存之后,第一次调用花费时间较多,后面4次花费时间很少,这是Server端仅仅在第一次调用了Web Service 操作,其它4次直接使用了缓存结果。

5. 查看CacheMonitor,显示如图8,从中可以看到Cache Hits有4次,而Cache Miss有1次,这正是因为只有第1次miss了缓存,后面的4次直接从缓存中取结果。


图8 CacheMonitor 统计页面
【转】在 WebSphere Application Server V6 中配置和使用 Web Service 缓存

3.2. 开发Web Service 客户端缓存例子

下面我们开始开发Web Service Client 缓存的应用,该应用做为一个客户端需要调用EchoServerCacheWeb的方法。为了说明测试效果,首先去掉EchoServerCacheWeb的缓存功能,删除里面的cachespec.xml配置文件即可。

1. 新建一个动态Web Project,名字是EchoClientCacheWeb,新建一个类EchoClient,并在该类增加一个testEchoServerCache方法,该方法跟上面类似连续调用Echo Web service 5次,如清单5所示。


清单5 EchoClientCacheWeb的testEchoServerCache方法

public String testEchoServerCache(String para)
{  
 int total = 5;
 EchoServiceProxy proxy = new EchoServiceProxy();
 long startTest = System.currentTimeMillis();
 for (int i = 0; i < total; i++)
 {
  try {
   long startTime = System.currentTimeMillis();
   proxy.echo(para);
   long endTime = System.currentTimeMillis();
   System.out.println("[EchoClient] the " + (i+1) + 

   " call spent time: " + (endTime - startTime));
  } catch (RemoteException e) {
   e.printStackTrace();
  }
 }
 long endTest = System.currentTimeMillis();
 System.out.println("[EchoClient] all test spent time: " + (endTest - startTest));
 return para;
}


将该方法发布为Web Service操作并产生WSDL文件。

2. 编写cachespec.xml文件放在WEB-INF目录下面,如清单6所示。


清单6 EchoClientCacheWeb的cachespec.xml文件

<?xml version="1.0" ?>
<!DOCTYPE cache SYSTEM "cachespec.dtd">
<cache>
 <cache-entry>
  <class>JAXRPCClient</class>
<name>http://localhost:9080/EchoServerCacheWeb/services/EchoService</name>
  <sharing-policy>not-shared</sharing-policy>
  <cache-id>
   <component id="Hash" type="SOAPEnvelope" />
   <timeout>10</timeout>
  </cache-id>
 </cache-entry>
</cache>


其中class元素值JAXRPCClient表示Web Service客户端的缓存。name元素值表示要调用的也就是需要缓存的远程Web Service 的endpoint地址,cache-id元素值表示用SOAP请求消息的hash值做为Cache ID。

3. EchoCacheTest Project的EchoTest类内增加一个方法testClientCache,调用上面的testEchoServerCache Web Service方法,执行该方法,在Server端SystemOut.log中可以看到测试结果如下: SystemOut O [EchoService] called, echo : hello
SystemOut O [EchoClient] the 1 call spent time: 1742
SystemOut O [EchoClient] the 2 call spent time: 20
SystemOut O [EchoClient] the 3 call spent time: 10
SystemOut O [EchoClient] the 4 call spent time: 20
SystemOut O [EchoClient] the 5 call spent time: 0
SystemOut O [EchoClient] all test spent time: 1792

4. 从该测试结果可以看到,仅仅第一次调用执行了echo操作,花费时间较长,其余4次都使用了缓存。与Web Service Server端缓存不同的是,后4次调用花费时间更短,因为EchoClientCacheWeb没有发出SOAP消息去调用Echo Web Service,而是直接使用了客户端缓存。这一点我们可以使用TCPMonitor跟踪到,在上面的testEchoServerCache方法内将proxy的endpoint地址改为http://localhost:9081/EchoServerCacheWeb/services/EchoService,同时将cachespec.xml的name元素值也改为该地址。启动TCPMonitor监视9081端口后,再次运行EchoTest. TestClientCache()后就会发现SOAP消息仅仅发出去了1次,而不是5次。

 

4. 总结

 

综上所述,WAS V6的动态缓存服务提供了对Web Service缓存的充分支持,使用缓存功能可以大大提高Web Service的效率,利用Web Service的优势,其中编写合理的cachespec.xml规则文件是关键。

当我们计划使用Web Service 缓存时候,需要考虑到哪些操作需要进行缓存哪些不需要,一般来说更新数据库的操作不能进行缓存,某些实时查询不需要缓存。而一般的查询都可以使用缓存功能,其中数据变化频繁的需要将timeout时间设置得短一点,数据很少变化的可以将timeout时间设置得长一点。

对于Web Service需要在Client端还是Server端进行缓存,需要考虑具体的应用以及网络状况。一般来说Web Service Server 缓存应用较多,因为它对调用的client不做要求,而Web Service Client 缓存需要Client也做为一个Web Service 存在。在真实的环境中,特别是网络环境复杂、带宽低、通信状况差的情况下,这时Web Service Client 缓存具有明显的优势,因为它可以减少和Server端交互的次数,从而具有较高的效率。