在WebSphere Application Server V6 中配置和使用Web Service缓存原文在这
在使用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中红线所示。
图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。
为了使用WAS的动态缓存功能,不需要改变Web Service实现的代码或者改变现有的配置,仅仅需要在开发完成Web Service后定义一个缓存规则文件cachespec.xml,该配置文件定义了需要缓存的对象,产生Cache ID的规则以及timeout失效时间等。同时WAS提供缓存的监控页面,允许用户查看缓存统计信息,清除缓存等。
启动WAS后,启动管理控制台,选择Servers → Application servers → server1 → Container services → Dynamic cache service,确保Enable service at server startup已经选中,如图3所示。
选择Servers → Application servers → server1 → Web Container Settings → Web container,选中Enable servlet caching。保存所做的修改,重启Server后,修改生效,如图4所示。
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所示:
4. 部署完成后,重启WAS,在浏览器输入地址http://localhost:9070/cachemonitor,就可以看到缓存的监控页面了,如图6所示:
该例子是比较简单的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)一步步的开发这个例子吧。
1. 新建一个动态Web Project,名字是EchoServerCacheWeb,在里面新建一个class,名字是com.ibm.cache.EchoService,该类仅有一个echo方法,将请求的String参数返回,为了测试效果该方法内部延时1秒,如清单1所示。完成以上操作后将该方法发布为Java Bean Web Service操作。
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所示:
<?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。
<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。
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次直接从缓存中取结果。
下面我们开始开发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次。
综上所述,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端交互的次数,从而具有较高的效率。