摘自 IBM WebSphere 开发者技术期?/font>?/p>
本文提供了一些常规指|以创够正常执行的 IBM WebSphere Portal 自定义代码。自定义代码不仅?PortletQ虽然它们是最常见的门LE模型)Q也包括 WebSphere Portal 的主题和外观代码。由于这些组件均采用?Portlet 相同的技术实玎ͼ所以很多相同的性能注意事项对它们也同样适用?/p>
对于 PortletQ本文主要集中讨论遵?Java?Portlet 规范 JSR 168 ?WebSphere Portal 中的对应实现的标准化 Portlet。本文基?WebSphere Portal V5.1 或更高版本,不过本文中所l出的准则和大部分徏议永q适用Q而不受所q行?WebSphere Portal 版本的媄响?/p>
本文说明如何设|和使用 Porlet 应用E序的部|参C优化门户?Portlet 的性能Q因是创定义门户代码的最后一步。不q?WebSphere Portal 的M优化Q即创徏q|自定义代码后执行的理操作Q将不在本文中讨论。另一个文档对 WebSphere Portal 性能优化q行了说明。该文档与本文档共同提供了门户与性能斚w不错的参考资料?/p>
本文旨在供参与构建门户应用程序ƈ希望提高对与自定义代码相关的潜在性能问题的理解的E序员、设计h员和架构师用?br />
IBM WebSphere Portal 构徏?the IBM WebSphere Application Server 产品之上。因此,自定义门户代码的~程环境h三重特征Q其对应的重要含义如下:
WebSphere Portal 及其所有组件均为基?Java 的程序?/b>
因此Qȝ来说Q应该遵循编写高性能 Java 代码的最佛_c?/p>
WebSphere Portal 是运行于应用E序服务器^C?J2EE 应用E序?/b>
J2EE 包含多线E技术;J2EE 容器通常采用每个h一个线E的Ҏ处理h负荷。对于用此机制必然涉及到的M实现或性能注意事项都应该加以注意?/p>
WebSphere Portal 提供?API 以扩展门户功能?/b>
可以采用很多Ҏ对Q务进行编E。应该首先考虑影响性能的差异?/p>
下一部分中将介绍一些关于门LE环境不同部分的一般性能注意事项?/p>
昄Q本部分q不会提供处?Java 性能的全部技术。我们仅在此处给出在我们认ؓq行 WebSphere Portal 开发时最有用的相关事,q提供一些可帮助深入了解 Java 性能的参考资料(请参?a >参考资?/font>Q?/p>
基本 Java 性能
在这一部分中,我们讨Z些应用到大部?Java 的一般性能V尽这些徏议可能ƈ不会带来大幅度的性能提高Q但可以使您对在开发阶D底层程序执行性能的重要性有所认识?/p>
?Java 中,String 对象是不可变的,?StringBuffer 对象是可变的。无Z时将文本附加?String 或从中删除,实际上都创Z个新对象Qƈ旧对象丢弃。因此我们首选以下的方式Q?/p>
以此为基的字W串联结操作Q?/p>
有时可以通过讄 StringBuffer 的初始容量进一步提高性能Q该cȝ设计使其可以在不能保存全部数据时自动扩大定w。不q此处有性能损失Q因?StringBuffer 必须透明地增加其大小和对数据UM。例如,如果?StringBuffer 作ؓ攉参数Q即向其添加越来越多的数据Q用,应该在对其进行初始化之前计算恰当的缓冲区大小Q以使其永远都不需要增加大?/p>
?I/O 操作期间臛_会阻塞当前线E;如果其他U程也必ȝ待磁盘,则系l响应时间将会迅速增大。除非在执行日志记录功能Q例如,在记录异常或站点讉K信息Q,否则 WebSphere Portal 自己不会引vM盘讉K。我们将在后面对 I/O q行q一步讨论?/p>
synchronized 关键字每ơ仅允许一个线E进入代码块。同步代码块所需的执行时间越长,其他U程{待q入该代码块的时间就长。我们将在后面对同步q行q一步讨论?/p>
例如Q?System.currentTimeMillis() 索当前时间信息开销q当大。如果确实需要时间信息,L定是需要当前准的旉Q还是(例如Q准到最q的U数p够了。如果在代码路径中有很多获取旉的调用,但ƈ非一定要毫秒U的准确度,可以采用替换ҎQ即定h开始的旉Q然后直接在h期间使用该信息?/p>
通常Q应?Java 中的异常用于指示错误情况。不要用异常指C操作成功,q主要是因ؓ JVM 创徏异常堆栈跟踪非常ҎQ而且?WebSphere Portal pȝ中的跟踪深度会很深?/p>
?API 为动态代码执行增加了功能强大的选项Q但方法执行时间而言Q获得这U灵zL会D严重的性能损失。通常Q应力避免在门户代码中使用 Java Reflection API。不q,如果有必要进行反调用,则应量其攄在初始方法中Q以使其在每个请求期间都不会执行?/p>
内存使用和垃圄?/b>
虽然内存对于 Java 客户Y仉常不是一个突出的性能问题Q但对于 J2EE 应用E序却是一个主要问题,q主要是因ؓ企业应用E序通常由很多用户同时访问。ؓ了应用E序服务器高效运行,可用资源Q包括内存、CPU 和带宽)均由客户机的h׃n。我们要提到三个主要内存问题Q?/p>
q意味着要尽可能地重用对象,而不要太频繁地创建新对象实例。创建的对象多QJVM 垃圾回收器就必须更频J地回收内存q会Q至部分)中断此时的请求处理。创多对象还Ҏ增加堆碎片,而这会导致出现更多的垃圾回收周期。例如,不要q早创徏对象Q?/p>
在本例中Q仅在对条gq行求g后才应创?logData。缓存和对象池技术均可以减少临时对象的创建。若要识别代码中最常导致内存分配问题的部分Q请参阅工具?/p>
不要太多信息读入内存中Q而要使用~存保存重要的信息。有时可以针对一条信息更Ҏ据类型。例如,数据信息可以保存?java.util.Date 对象内或 long 变量中。与基元数据cd相比Q对象通常更大Q处理速度也会有些慢。它可能会依赖于邻近?API 和数据类型首选的数据l构。通常Q内存占用率高Q就会导致垃圑֛收率更高Q请求处理期间暂停的ơ数也会增加?/p>
内存泄漏通常出现?Java 集合cM。例如,如果有一?java.util.MapQ在特定情况下,会将数据d到映中Q但却永q不从其中删除。内存泄漏会D Java 堆保留的内存使用来大Q随着旉的增加,垃圾回收器能释放的内存会来少。这P会导致垃圑֛收更频繁Q而最l将佉KLl停止响应。而更p糕的是Q通常仅在长时间运行的试中才能发现内存泄漏,不过可以使用各种工具帮助q行此类分析Q请参阅工具Q?/p>
性能和可扩展性代码设?/b>
设计和开发可伸羃性代码时Q需要记住很多事V其中最为重要的三方面是Q缓存、对象池和信息预提取Q?/p>
例如Q可以从后端pȝ索信息,但不每个可能的对象均从存储区复制到内存中,而仅加蝲其中的小部分Q将其放|在~存中。这P该信息就对稍后的引用可用Q可能在后箋的另一h中用,甚至供另一个用户用)?/p>
~存始终采用对象映射的Ş式,h大小上限。缓存还必须知道不可能再ơ请求某个内容的情况Q以便在合适时从缓存中其删除。这U排除操作通常由“生存时间?TTL) 或“最q最用”算法确定。而且使用~存的客h不能保证成功从~存索对象;必须首先查对象是否存在,如果没有扑ֈQ则创对象Q?/p>
Q在某些情况下,特定于应用程序的~存可以设计Z对客h透明的某个数据源查找所需的数据。) 每个h都需要特定类的实例,但此对象q不Q也不应Q需要在每个h中重新创建。在对象创徏和初始化开销很大的情况下Q尤其是q样。客h可以不接受性能命中Q而从池中h对象Q然后在用完之后其q回池中?/p>
q意味着对象的所有不同实例在E序初始化阶D创建,在随后重用和引用。java.lang.Boolean cd是已规范化对象的例子。只需要有两种不同?Boolean 对象卛_Q最好能作ؓ常数讉KQ。同P也可使其他对象用一l固定的只读内部状态?/p>
例如Q在 Portlet 中,可以提供一个电子邮件列表;?Porlet 显CZ题、日期、发件h和其他重要信息。当用户选择了特定的电子邮gӞ显C邮g的正文。在?Porlet 中选择特定的项之前Q不需要正文,因此提前索正文将费执行旉和内存资源。这U模式在很多情况下都适用。ȝ原则是,仅计和索对于当前请求和响应有直接意义的信息?/p>
IBM WebSphere Application Server ?J2EE 实现QWebSphere Portal 构Z其上。由于本部分中很多性能注意事项适用?J2EE q行时上下文Q所以其中的很多信息除了适用?WebSphere Application Server 之外Q也适用于其他应用程序服务器?J2EE 应用E序。下面所列出的项目在此处只进行了单概qͼ在后面q行更ؓ详细的说明。有x多的一般性讨论,请参?a >参考资?/font>
J2EE 标准
J2EE 标准规范包含了大量与性能相关的事: 应当使用初始Ҏ计算所有后面将用到且不会发生更改的内容Q很?J2EE 资源都可以用初始方法,Portlet 也可以用此cL法)。例如,数据源等普通资源的 JNDI E序应该仅在初始化时执行一ơ。此外,也应该仅?Portlet 初始化期间读取一ơ来自特定只L件的数据。可以对 Portlet 服务Ҏq行扫描Q以发现所有对每个h执行相同操作的代码,其Ud到初始方法中Q以降低该服务方法的q行时开销?/p>
EJB 和会话是 J2EE 中非帔R要且功能强大的概念,但如果用不当,二者均可能D性能损失。例如,应用E序不应过多的数据攄C话中Q从而减服务器的内存占用ƈ更快速方便地保持会话。关?EJB lgQ应该熟悉与q程调用和本地调用等相关的不同持久类型。EJB 可以使用的某些功能会带来大的性能损失?/p>
WebSphere Application Server
WebSphere Application Server 产品提供了各U功能,以帮助开发h员和架构师设计高性能pȝ。(请参?a >参考资?/font>中给出的 WebSphere Application Server 信息中心?WebSphere Business Integration Server Foundation 信息中心Q?/p>
下面所l的CZ演示了多个请求对q接q行重用。在本例中,可以使用 JDBC q接池和利用 Application Server 提供的语句缓存: WebSphere Application Server q支持对象池的常规概念,每个对象池均h池管理器Q从而ؓ不同cȝ型提供对象池讉K。可以查询此cd象池以获得类cd实例Q如前面关于池技术的CZ中所q。请参阅 WebSphere Business Integrator Server Foundation 信息中心以获得详l信息?/p>
WebSphere Application Server q提供了“一般用途”的~存。在理控制CQ可以定义缓存实例,应用E序可以使用q些~存实例存储、检索和׃n数据。与~省׃n动态缓存(门户使用其缓存对象)不同Q缓存实例仅可由知道?JNDI 名称的应用程序访问。DistributedMap cL应用E序所使用的编E接口,该类允许应用E序从缓存实例获得对象和对象放|到其中Qƈ可以使其失效。请参阅 WebSphere Business Integrator Server Foundation 信息中心以获得详l信?[8] (LINK)?/p>
如果 Portlet 使用~存实现Q它们应该在其初始阶D|找或实例化一个缓存实例,q保持该~存的引用,以ɾ~存条目h可能比单个请求长的生存期。在处理 Portlet 的操作和呈现阶段Ӟ可以条目放|到~存中,q从中进行检索。Portlet 实现需要确保如果用特定键查询Ӟ~存没有q回数据Q应有适当的后端访问和~存q行更新处理。另外,q要注意Qؓ了实现设计的特定功能Q可能需要限定键在缓存中的范_如,Z用户会话Q。缓存通常我管理的单元Q根据缓存实玎ͼ可以排除条目或其失效。请注意Q出于同L原因Q缓存ƈ不适合在多D代码间q行信息通信。缓存还应该l护一个合理的大小上限Q以避免自定义代码中内存的过度用?/p>
WebSphere Portal 支持两种不同?Portlet APIQ?/p>
在本文中Q我们将重点讨论 JSR 168 Portlet API?/p>
WebSphere Portal 提供了各U接口,用于?Portlet 集成?WebSphere Portal 环境中。因此,应该谨慎设计 PortletQ以充分利用各种门户功能。请保采用最佛_践(请参?a >参考资?/font>中列出的最佛_践)Q以应用恰当?WebSphere Portal API?br /> 在本部分中,我们讨Z主题和外观编E以?Portlet 开发相关的性能主题?/p>
JavaServer Page (JSP) ?Portlet ~程的基之一。在大多?Portlet 中,JSP 通过使用 Model View Controller (MVC) 作ؓ视图lg使用。JSP ?HTMLQ或其他标记语言Q组合和 Java 代码l成Q在大多?HTML 中,它们的处理输Z是标记语a。其最单的形式中,JSP 不包含Q?Java 代码Q但仅包含自定义标记Q调用这些标C执行?HTML 操作。(相反圎ͼJSP 文g中也可能不包含Q?HTML 内容Q?/p>
q与其他生成 HTML 内容的方法(XML ?XSLTQ不一栗用其他方法时Q对于每ơ请求都必须解析 XML 和应用样式表转换。只有很好地~存了结果而不需每次h都重新运行{换,才能保证性能。因此,从性能的角度出发,JSP 应该优于 XML/XSLT。此外,门户基础设施q针?JSP q行了优化,允许方便地扩展以支持其他标记、语a和浏览器?/p> 应用E序服务器执?JSP 的方式与执行常规 Servlet cM。不q,JSP ~译产生?Servlet 包含生成的代码,q些代码的性能优化E度E逊于手动~写的代码。如果性能对于特定 JSP 非常重要Q而用生成的代码又不能达到目的,误虑手动标记编写到输出中?/p>
JSP 中的 Java 代码片断UCؓ Scriptlet。由?JSP {换ؓ Java 源代码,因此使用 Scriptilet q没有真正的性能损失。WebSphere Application Server 的最新版本中的某些优化将?JSP 文g不包含Q?Scriptlet 的情况下应用。通常Q不应将 Scriptlet 代码攄?JSP 中,而应使用标记完成q些d?/p>
JSP 中可以包含其?JSP。这意味着单个 JSP 不必对请求作出全部响应;可以响应拆分ؓ多个 JSPQ在?JSP 中包括其?JSP。有两种包含方式Q静态包含和动态包含: 静?JSP 包含在编译时解析。JSP ~译器会包含所引用的文Ӟ而不包含 include 语句。此选项通常非常快,完全不会增加q行时开销?/p>
动?JSP 包含在运行时解析Q开销q不。就垃圾生成和执行时间而言Q解析要调度的正?JSP 开销非常大。例如(?JSP 中)Q?/p>
JSP 中的动态包含在通过 Servlet 代码包含其他文g时用如下语句: 因此Q只要有可能Q应该尽量用静态包含。动态包含提供了最高的灉|性,但如果用过于频J,会带来巨大的性能开销?/p>
Enterprise JavaBean (EJB) 定义了一个基于组件的体系l构Q用于构建可扩展的分布式多用户业务应用程序。EJB lg设计用于装业务逻辑Qƈ同时所有的复杂性隐藏在 Bean 和内|?EJB 容器服务后?/p>
对企业应用程序频J用的各种功能的支持会带来一定的性能开销Q在使用 EJB 旉要加以考虑?/p>
Portlet 可以通过 JNDI 查询包含 EJB 引用Q?JNDI 查询在性能斚w开销很大。例如,如果 Portlet q不~存?EJB L口的引用Q则每个?EJB 的逻辑引用需要两ơ远E调用:一个调用命名服务,另一个调用实际的对象。ؓ了改q这U情况,请用缓存技术以减少或消除对 EJB d用的重复查询?/p>
EJB lg公开q程接口和本地接口。依赖于位置?EJB 用远E接口。方法参数和q回值将?RMI-IIOP 上序列化Qƈ由D回。远E方法必设计ؓ能够Ҏ API 的用模式满x据需求。请使用 API 中的适合接口的用情늚Ҏ和数据类型粒度,以尽可能减少序列化开销? 可能减远E调用的数量Q以减少׃代码路径中的q程调用带来的系l开销。用会?Bean 作ؓq程外观使用Q对复杂交互q行包装Qƈ减少 Portlet 和域对象间的q程调用。直接访问远E实?Bean ?Portlet 通常会导致多个远E方法调用。如果在此环境中使用实体 BeanQ请避免l予其远E接口。作为外观的会话 Bean 通过其本地接口访问实?BeanQ从其收集数据,然后此信息q回发出调用的应用程序?/p>
当发用的客户机(如会话外观)与被调用?EJB ׃n同一个容器时Q本地接口的概念会有效果。用本地接口可以消除分布式对象协议的系l开销Q从而降低进E间通信开销。本地调用ƈ不会通过通信层,所有对象均可以通过引用传递?/p>
EJB 容器支持的事务管理也可以影响性能。开发了 EJB 后,E序员必设|定义各U特征(?EJB 的事务支持和隔离U别Q的部v描述W。如果不需要事务,请将事务cd讄?NotSupported?/p>
事务隔离U别是基数据库将已更改但未提交的数据向其他事务公开的程度。ؓ了获得最佳的性能Q请使用自由隔离U别。不q,让其事务看到未提交的数据可以带来意料之外的副作用Q如更新冲突和读取不一致等。有兛_何设|隔ȝ别的说明Q请参阅 WebSphere Application Server V5.1.x 信息中心?/p>
请参?IBM 白皮?WebSphere Application Server Development 性能和扩展性最佛_?/font>?IBM U皮?IBM WebSphere V5.1 性能、扩展性和高可用?WebSphere 手册pdQ以获得其他以及关于每个的相关理由?/p>
标记大小指从门户服务器传输到客户机的完全呈现门户面的字节数量。从门户服务器的角度来看Q最重要的部分是包含l果标记?HTML 面的大。也必须其他文Ӟ如样式表、图像或 JavaScriptQ传输到客户机。由于静态文仉常保存?HTTP 服务器或代理~存上的门户pȝ之外Q所以,此处我们主要讨论“真正的”HTML 标记大小?/p>
到底Z什么原因非得关注标记大呢Q在公司的内部网内,|络带宽的问题可能会一些,但如果用户通过调制解调器或其他低带宽网l连接到门户Q大?HTML 响应很长的下载时间可能会令h非常受不了?/p>
让我们进行一个简单的计算。假设服务器或集每U钟最多能处理 100 个请求。HTML 面大小应该?100KBQ这个D然看着很大Q但如果在页面上有复杂的主题和若q个 PortletQ就很容易达到这个大。对于服务器Q这意味着必须提供U?10MB/sec 的速度Q?00 KB * 100 面/U)。而这个值是 100MB 的网l可以处理的最大通信量。(以太|不可能癑ֈ之百地支持其 100MB/sec 的速度Q且传入通信量也不容忽视。对于通过 56K 调制解调器连接到门户的用P每个面的下载时间应?15 U的旉范围内!Q?/p>
多大能称为太大?q个问题通常很难回答。不q,每个 HTML 面的大超q?100KB 可能太大了。另外,q要CQ较的讑֤对其可以处理的每个请求的标记大小有一定的限制?/p>
构成标记大小的主要内Ҏ主题?Portlet 输出。由于所有门?JSP 均可自定义,所以可以改变标记的在终端的紧凑E度。要限制标记大小Q可以采取以下措施:
?JSP 中?JSP 注释Q而不?HTML 注释?/b>
JSP ~译器将删除H体注释 <%-- ... --%>Q而保留窗体注?<!-- ... -->q将光过|络传输?/p>
量减少 JSP 源文件中的空白、制表符和分行符Q因?JSP ~译器将会保留这些内宏V?/b>
q可能会降低代码的可L。这些内容可以帮助开发布局良好的代码,但在 JSP 文g应用到生产环境前Q将使用工具对其q行处理Q除d格式讄?/p>
量避免多次向客h发送相同的信息?/b>
例如Q样式定义应当放入独立的 CSS 文g中。JavaScript 代码也应如此。而且Q由于这些独立的文g通常不会更改Q因此可以将其缓存在览器或代理~存中,从而进一步减网l通信量?/p>
如果您的环境讄为支持压~,q可以?HTTP 压羃压~过的标记发送到的客h?/b>
请参?Web 服务器和客户机的文档Q以获得详细信息?/p>
日志通常最l会涉及到对盘写入。从性能的角度而言QQ何与盘频繁q行交互的内定w是潜在的大开销操作Q因此,最好尽量减在生环境中?Java I/O 库。由于通常通过使用某些 Java ~程之下的本机库提供 I/OQ因此会有一定的~省pȝ开销。System.out.println 之类的操作在文g I/O 期间会对处理q行同步Q这对性能造成很大的媄响?/p>
在开发和试模式中,可能希望所有日志记录和调试功能均ؓzd状态,因ؓq些功能对于发现错误非常重要。在生环境中部|应用程序时Q让各种日志功能均处于打开状态ƈ非可行的选择。最佛_践应是对日志语句加以保护Q其仅在出错和q行调试的情况下打开。可以通过使用一个最l的 Boolean 变量实现此功能,当将其D|ؓ false Ӟ可以有效地指C编译器q行优化Q不再检查和执行日志记录代码Q?/p>
Java 语言提供了两U流Q读取器/写入器和输入/输出Q?/p>
d?写入器有性能开销Q因为它们旨在用于字W流Q且会在后台数据编码ؓ字节。只要希望操作二q制数据Q就应该使用输入/输出?/p>
Z可能提?I/O 性能Q应该对d和写入操作进行缓存。如果希望写入大量来?Portlet 的数据,通常最好采用对已缓存的数据q行部分h的方式,而不采用对全部数据一ơ性刷新的方式。另一斚wQ不要太频繁地刷新缓冲区?/p>
用于协调对共享对象的讉K?Java 机制UCؓ同步。同步语句一ơ仅允许一个线E进入代码块?/p>
?Portlet 的生存期中,容器会将不同U程中的服务h发送到单个 Portlet 实例。请避免?Portlet 中进行同步,因ؓ同步有很大的性能影响Q同步会减少q发Q因为在同步块中一ơ仅允许q行一个线E,所有ƈ发的U程都要q行排队。另外,Java 虚拟Z使用监视器以支持同步Q管理这些监视器也有性能开销。除了性能影响之外Q还可能出现死锁Q而这可能D单个 Portlet ȝQ或者甚xp,D整个门户ȝ。由于监视器不支持进行Q何死锁处理,因此E序员应负责防止死锁的出现?/p>
在有必要q行同步的场合下Q应该尽量羃同步代码块。准地识别哪些代码真正需要同步ƈ可能少地进行同步,q非帔R要。如果同步代码块不够,应该对代码进行分析,对其重构Q以使所有可以异步运行的代码均位于同步代码块之外?/p>
某些 Java J2SE 功能会间接地使用同步。Java 集合c(?Vector ?HashtableQ都是全面同步的。即使在单线E环境中 Java E序也会有与U程同步相关的开销。Java 1.2 引入的较新的集合Q如 ArrayListQƈ不进行同步。这提供了Ҏ据更快的讉K。在需要线E安全的情况下,请用线E安全视图。线E安全视图是包装c,该类增加了同步标准集合方法的功能。集合类的工厂方法将q回U程安全的集合,该集合由特定的集合类型的实例支持Q?/p>
另一个非直接同步的例子就?Java I/O 库。请可能少C?Java I/O 库方法(例如 System.out.println()Q,以减不必要的性能开销?/p>
不要?Portlet 生成非托线E。当?J2EE 强烈不要试图在容器生成新U程。实际上QJ2EE 规范 6.2.1 ~程限制指出Q?/p>
“如果应用程序组件包含的功能?J2EE pȝ基础l构所提供的功能相同,则会存在功能冲突和管理乱。例如,……以理U程……?/i>
不要试图生成新线E的一个实际原因是因ؓ新线E对 J2EE 上下文没有完全访问权限。而且Q新创徏的非托管U程会妨?WebSphere Portal 实现E_的、优化的可扩展运行时环境。因此,请?WebSphere Application Server 中的异步 Bean 功能Q请参阅 WebSphere Application Server Enterprise V5 和编E模型扩?WebSphere 手册pdQ。异?Bean 是一?Java 对象?Enterprise BeanQ能够?J2EE 上下文提交在独立U程Q异步)q行的代码?/p>
Portlet ~程模型允许开发h员创建特定类型的 Web 应用E序Q此cd用程序可以作为客h览器中若干此类应用E序的聚合视囄一部分。在 WebSphere Portal 中,此类应用E序不仅能共存在一个页面(卌合视图)上,q能在构造该面时彼此进行通信。因此,Portlet 的实现可以媄响页面的M性能Q例如,如果特定的“关键”Portlet ȝ在页面上Q则值得q力在同一个页面上实现一些其他的关键性能 Portlet?/p>
在实际的门户中,完全自我依赖?Portlet 非常见Q因为门户通常用作|站的附加内Ҏ帮助工具。此c?Portlet 应仅在其本地代码执行路径中优化,不应对允许的门户pȝ带来太多的负荗?/p>
Portlet 更ؓ典型的用户就是提供需要访问其他数据源或事务系l的应用E序功能Q除?Portlet 的原始执行系l之外,q些数据源或事务pȝ也需要执行资源。数据可能会从网l上的其他后端系l检索或存储到其中。需要在Mpȝ设计中考虑在后端系l上可能出现的事务长度、隔ȝ别以及数据锁定?/p>
h意,单个 Portlet 可能不是后端pȝ的唯一客户机。事实上Q在实际使用中,会有很多客户接到此类pȝQ甚臛_?Portlet q可能同时多ơ访问同一个后端系l。Portlet 可能会在多个独立的服务器U程中执行其代码以响应不同的用户h。因此,有必要对讉K模式q行了解QPortlet 或其他客h获取事务或锁定的方式可能会媄响此cd端系l的q_响应旉?/p>
如果某个 Portlet 在操作或呈现阶段需要进行密集的后端pȝ讉KQ响应时_完成q些阶段的时_越来越依赖于后端系l的响应。(如果{待门户服务器外的响应以满传入hQ将会带来gq,此gq不能通过优化 Portlet 代码的执行\径得到改善。)h后端pȝ通信的良好设计,q了解事务行为通常可以得到更高的性能?/p>
Z避免׃后端pȝ崩溃而 PortletQ以及其所在页面)停止响应Q可以在代码中加入超时机Ӟ不过Q请注意Q管理和跟踪旉戳会带来一些处理开销。如果用了 WebSphere Portal 中的q行 Porlet 呈现功能Q稍后讨论)Q则可ؓq行呈现U程配置时?/p>
可能减与此类外部后端pȝ的交互和数据通信量也是不错的做法。ؓ了实现这一点,如果信息的刷新标准允许进行缓存,Portlet 可以对信息进行缓存。这可以减少为每个传?WebSphere Portal h多次获取相同数据的往q次数。这栯可以帮助降低后端pȝ上的负蝲Q因样就无需多次提供相同的信息了。另外,如果不需要在|络上传输数据,Portlet 可能可以更快地进行呈现?/p>
避免到后端系l的往q的另一个方法就是除了检索满_前请求实际所需的数据外Q还索所知的在可能的后l请求中所需的数据。不q,使用此方法时Q如果知道在后箋h中将要实际需要哪些预提取数据Q我们仍然徏议用普通的预提取功能。ؓ了合理地设计此特性,需要对 Porlet 应用E序的典型用户交互非怺解。要CQ提前检索会寚w?JVM 的内存用造成影响。(请参?a >性能和可伸羃性代码设?/font>。)此类设计Ҏ可能需要更改后端系l的接口Q但可以节约大量的处理时_使得更改物超所倹{?/p>
对于~存QWebSphere Application Server 利用光向的 Portlet ?DistributedMap 接口提供了动态缓存功能。(请参?WebSphere Application Server 5.1 信息中心以获得更多的信息。) 保持和维?Portlet 的数据,使其生存期长于单个请求的生存期,q是一个典型的 Portlet ~程d。通常考虑采用的第一个方法就是?PortletSession。从E序员的角度而言QPortletSession 使用很方便,但从应用E序服务器的角度而言Q管理会话需要用资源。如果会话包含越来越多的数据Q从而要求用更多的内存Q则会进一步问题严重化?/p>
如果会话配|ؓ持久地存储在数据库中Q或配置行内存到内存复制Q即在集化环境中ؓ WebSphere Portal 配置了故障{U)Q则该会话将在其内容更改时被序列化?/p>
当会话数据写入到q程副本Ӟ对会话数据进行序列化和反序列化所需的时间可能变得非常大。在非常见的情况下Q存储在会话中的某些对象可能被标Cؓ瞬态的。这降低会话的序列化后的大,但不会更改内存的大小Q而这对应用程序服务器处理会话的效率也有媄响?/p>
大型的会话对象会减少可用以创建和执行应用E序对象?JVM 内存。因此,随着可用堆内存的减少而导致更频繁的垃圑֛Ӟ性能可能会降低?/p>
另一个因素就是内存内的生存期比所需的用时间长Q因此占?Java 堆中的空间的会话数量通常比活动用L数量多。在 WebSphere Application Server 中可以配|会话过期时_q个属性非常必要,可以防止在几U钟没有zd后就要求用户再次d的情c会话的释放?WebSphere Application Server ?Portlet 容器负责?/p>
序列化的会话大小应该于 4KBQ因?WebSphere Application Server 能以可以接受的数据库性能开销存储此类会话Q在|络上传输此cM话的旉也更。如果会话大超q了 32KBQ数据库必须使用面向二进制大对象配置的表单元|而如果此cM话从数据库检索或写入到数据库中,则将需要访问物理磁盘(对于大多数受支持的数据库Q?/p>
׃上分析得出的W一个结论就是,从应用程序的角度而言Q应该尽可能避免创徏会话。在大多数公共页面和无需w䆾验证的页面上Q通常不需要会话。在此类面上可以通过呈现链接与门戯行交互,而呈现链接定义ؓ不更Ҏ务器端的状态。门户将为每?Portlet l护呈现参数Q以用于对该面的所有后l请求。ؓ了避?JSP ~省创徏会话Q应该将 JSP 中的面会话指o讄?falseQ?/p>
否则Q如果不存在会话Q此 JSP 创Z个会话?/p>
以下?Java 代码片段演示了如何确保传入会话加入现有的会话Q而不是无条g地创建新会话Q?/p>
此参数的D|ؓ false Ӟ如果之前不存在会话,不会创Z话。如果之前不存在会话Q仅Z在其中存储数据而在 Portlet 中创Z个会话,可能q不合适?/p>
׃上分析得到的W二个结论就是,?/b>要将会话误用作通用数据存储机制。请CQ我们的目的是尽可能使会话保持最。如果由?Portlet 的设计,某些数据保存在内存中具有一定优势,则可以用缓存。可以用会?ID 讄~存条目的范_以会话和要保存在内存中的数据徏立关联。请注意Q此cȝ存在呈现故障转移时不支持集群Q而这有时是可以接受的折衷。如果数据可以用其?Portlet 可用数据重新创徏Q则~存条目的会话范围要求就有待商榷?/p>
在很多情况下Q通过仅在会话中存储一个键Qƈ使用该键作ؓ引用以在其他数据l构中查找更大的对象Q从而可以避免在会话中存储大对象。另外,可以选择使用相同信息的更紧凑的表CŞ式,而后该对象攑օ会话中?/p>
而且QPortlet 设计需要仔l考虑会话中实际存储的内容。会话通常仅旨在用于存储用户交互与门户应用E序的对话状态(例如Q网上商?Porltet 中的购物车的内容Q。此cL据不能采用其他Q何手D重新创建。在 WebSphere Portal 中,q种cd的数据处理称?b>会话状?/b>?/p>
如果q不需要会话状态,Portlet 可以使用其他数据存储选项Q?/p>
?Portlet 的操作阶D,可以?Portlet 的后l呈现阶D设|呈现参数。Portlet 使用呈现参数呈现其特定于一l特定值的视图。由容器在请求间l护呈现参数Q即使出C其他 Portlet 的交互也是如此。在 WebSphere Portal 中,q种cd的数据处理称?b>D状?/b>?/p>
如果需要跨多个用户会话保持数据Q则可以使用 PortletPreferences API ?Portlet 存储数据。请CQ此 API q不能替代通用数据库。在 WebSphere Portal 中,q种cd的数据处理称?b>持久性状?/b>?/p>
PortletConfig API ?Portlet 可以d光|,该配|由开发h员通过使用 Portlet 部v描述W提供;q对?Porltet 的所有用户均有效?/p>
PortletContext API 允许存储同一应用E序中其?Portlet 也可以访问的属性?/p>
误虑使用会话之外的其他选择Q将其用于存?Portlet 创徏和用的数据。避免将可以通过用户交互之外的其他源重新创徏的数据复制到会话中?/p>
与对特定?Portlet 视图d相比Q用呈现参数有很多优势?/p>
如果 WebSphere Portal 到?Portlet 的操作参敎ͼ则必调用特D的操作阶段处理Q其具有不必用操作参数的优势。不q,h意,处理呈现链接时一定不能更?Portlet 的服务器端状态。要更改服务器端状态,唯一得到认可的方法就是用操作链接,而对于事务类型的hQ操作链接是最好的选择?/p>
使用呈现链接而不使用操作链接的例子很多。例如,假设一个报U?Portlet 可以同用“上一”和“下一”按钮显C特定的面。逐页览报纸的页面不一定会更改服务器端的状态,此状态在本例中就是报U怸包含的全部信息。ؓ了寻址报纸的下一,下一늚的页码编码到所昄按钮的呈现链接中p够了。Portlet 可以Ҏ呈现参数中所l的늠定要呈现的面?/p>
此外Q由于每个呈现的视图都由独立?URL dQ所以,通过使用呈现链接而不使用操作链接Q还可以充分利用~存基础l构Q无论是览器缓存还是代理缓存)。URL 是用于访问此cȝ存基l构中的特定生成视图的唯一的键?/p>
接下来的几个部分中将讨论开发h员应该考虑?WebSphere Portal 中可用的一?Porlet 优化功能Q这些功能可以媄响所选择的实现技术。需要?Portlet 的部|描q符提供一些必ȝ讄Q而且Q由于这些项也是?Portlet 开发h员提供的Q因此被认ؓ是自定义代码?/p>
允许 Portlet q行q行呈现
WebSphere Portal 提供了让面上的 Portlet q行呈现的选项。此功能q完全“免费”的Q因为需要计资源以l护和管理呈现每?Portlet 所使用的不同线E?/p>
如果涉及到很多后端系l,而每个后端系l在呈现单个面旉会生gq,此时使用q行 Portlet 呈现具有一定优ѝ例如,假设一个门户页面包含很?PortletQ每?Portlet 都会讉K不同的后端系l。在串行呈现模式中,从所有后端系l检索所需数据的M延迟为各个gq时间的d。而在q行呈现模式中,延迟旉应ؓ所有单个gq时间中的最大倹{?/p>
如果 Portlet q不l常使用后端pȝQ由于启用ƈ?Portlet 呈现所带来的开销可能会比由此功能所带来的好处更大。如果页面上?Portlet 能够独立于后端系l进行呈玎ͼ则只需要门h务器计算机本地的 CPU 资源。这U情况下Q页面呈现响应时间不会得到改q?/p>
可以使用囑Ş用户界面、部|描q符?WebSphere Portal ?XML 讉K接口启用q行 Portlet 呈现。而且Q还有一个相关的全局属性|可以全面开启和关闭q行 Portlet 呈现功能?/p>
要正回{是否支持ƈ?Portlet 呈现门户q一问题Q需要考虑若干事项Q例如,呈现面所涉及到的后端pȝ的数量、用ƈ?Portlet 呈现的页面上?Portlet 的^均数量,{等。Portlet 开发h员事先不一定能l出q些问题{案Q但如果合理的话Q开发h员当然事先可以确保ؓ Portlet 启用了ƈ?Portlet 呈现?/p>
?Portlet 容器中进行缓?/b>
Z Portlet ?Web 面是动态聚合的Q因为它们能以个性化的方式提供动态内宏V这个灵zL具有一定的开销。由于ؓ了响应请求生成这些页面必进行额外的工作Q故而网站的响应旉增加?/p>
新的~存技术将改善动态页面的生成和减系l负载。WebSphere Portal 支持片断~存Q也UCؓ Servlet ~存Q,可以使用 WebSphere Application Server 动态缓存在~存中保?Portlet 输出。对~存?Portlet 的请求将从缓存(而不?PortletQ检索内宏V可以通过在部|描q符中指定过期实现片断缓存的失效。而且Q在 Portlet 的操作阶D也会片断~存条目失效?/p>
Ȁzȝ断缓存不需要进行费时的安装和集成工作。通过使用单的 XML 部v描述W文件和通过使用 WebSphere Application Server 理控制台均可以启用和禁用该~存功能。(请参?WebSphere Portal 信息中心Q以了解?WebSphere Application Server 中启?Servlet ~存的详l信息。) Z使用Zq期的缓存,Portlet 必须在部|描q符 portlet.xmlQ对于符?JSR 168 规范的标准化 PortletQ中定义q期~存的持l时_
整数定义~存条目在缓存中存在的秒数倹{?/p>
?-1 指示 Portlet ~存永远不过期?/p>
?0 指示 Portlet 用~存功能?/p>
一定不能在同一 Portlet 的所有用户间׃n~存的条目。此~存技术是Z特定 Portlet 的特定用L?/p>
对于在其部v描述W中定义了过期缓存的 JSR 168 PortletQPortlet H口可以在运行时通过讄 RenderResponse 中的 EXPIRATION_CACHE 属性修改过期时_如下所C:
对于在从后端Q如 EJB lg和数据库Q计其响应和请求数据时计算旉很长的复?PortletQ此Ҏ非常有用。对于简?PortletQ不应启用片断缓存。WebSphere Portal 用额外的执行资源计算机片断缓存的内部~存键。对于简?PortletQ由于缓存键计算比重新计?Portlet 响应开销更大Q其性能可能会降低?/p>
对于真正动态的 PortletQ片断缓存ƈ不适用Q如Q对每个h都需要从其他数据源收集当前数据的Z实时?Portlet 或对每个h都会更改其响应标记的 Portlet。这会D大量的缓存失效,因此性能不会得到提高。所以,仅在 Portlet 的输出在更新前会在一D|间内保持有效的情况下才应该ؓ Portlet 启用~存功能?/p>
在远E缓存中q行~存
通过独特的自适应~存功能QWebSphere Portal 可以在门L存之外的~存Q称E缓存)中动态缓存生成的面Q如果所有页面组件均指示自n可以~存Q。如果从q程提供完全呈现的页面,可以避免到门户服务器的往q,此类面的响应时间可以与从静态网站提供时一样快?/p>
有关q程~存的全部详l信息,请参?a >使用 WebSphere Portal V5.1 开发包含静态内容和动态内容的高性能|站?/p>
PortletQ以及主题)可以提供完全呈现面的Mq程~存信息中其所特定的远E缓存信息。远E缓存信息一个数据结构,由关于缓存范_是否可缓存,是共享的Q还是非׃n的)和过期时_内容在多长时间内为有效)的信息组成。可以通过部v描述W或 WebSphere Portal GUI 提供 Portlet 的远E缓存信息。除此之外,Portlet q可以在呈现时ؓ每个 Portlet 提供q程~存信息Q如下面的代码中所C:
讄q程~存信息的方式依赖于呈现的视囄“刷新”要求和范围。请注意Q如果从~存提供呈现的页面,h可能甚至不会发送到门户服务器?/p>
如果可以在基l构中用缓存,自定?Portlet 开发h员应当考虑利用q程~存功能?br /> 在门h语中Q主题是定门户应用E序的外观和风格的若q?JSP 集。由于主题由 JSP l成Q在 JSP 部分l出的技巧也适用于此。这一部分详细讨论了用组成主题的 JSP 文g集可能存在的性能~陷?/p>
通常Q主题由很多不同的文件组成,每个文g提供屏幕的特定区域的内容。尽可以动态地包含 JSPQ但通常Q也Q将 JSP 静态包含在其他 JSP 中?/p>
׃~译时可能会很?JSP 包含到其?JSP 中,所得的 Java 源代码和 Servlet 字节代码文g可能会非常大。用大的类文g通常不会有性能问题Q但׃ Java ~程语言中包含的大小限制Q可能不能将 Java 源代码编译ؓcR例如,Java 中的Ҏ的大不能超q?64KB。大型的复杂主题很容易达到这个限Ӟ而导致不再能~译。这U情况下Q有三种选择Q?/p>
用动态包含代替一些(而非全部Q静态包含?/b>
?JSP 部分 (LINK) 提到的,q是用性能作ؓ交换Q以便能~译 JSP。从性能的角度而言Q尽这个方法最易于实现Q但却是最不好的解x法?/p>
量限制 JSP ?Scriptlet 的用?/b>
WebSphere Application Server 可以对仅调用标记处理E序的代码进行优化,而这可以有助于文g保持?64KB 的上限之内?/p>
清除 JSP 代码?/b>
q些文g通常包含q必要的多余代码。通常删除 HTML 注释行或I白Q或者将 JavaScript 代码Ud到单独的文g中均可保证够的I间?/p>
主题有时会完成应用程序中复杂的Q务。不q此时应该}慎。请CQ对于门L每个hQ都会呈C题,因此不要在其中进行会l系l带来高负荷的计工作?/p>
在模拟门户功能时要特别}慎。例如,主题可能会@环访问门户应用程序中的大量页面;应该Ҏq行{选,仅向用户昄一个导航结构,其中仅包含主题从门户 API h的若q页面。这U情况下Q门户中q行的很多处理都会丢失,因ؓ之后会将其结果丢弃。此处根据门戯问控制或个性化规则q行{选会更ؓ有效?/p>
此外Q要量限制门户面中门戯源链接的数量。门户必ȝ成的每个 URL 链接都会l系l带来额外的负蝲。如果需要具有大量链接的应用E序主题Q请试~存其中的一些页面,从而其不必在每次h旉重新计算所有链接?/p>
主题也是 WebSphere Portal 中的q程~存基础l构的一部分。主题的q程~存是一l可以通过 XML 讉K具体指定的元数据Q如以下CZ中所C:
主题不能提供M呈现时远E缓存信息?/p>
WebSphere Portal 支持高性能外观的识别。这些外观非常特D,因ؓ它们不是Z JSP 生成的;它们的输出是Ҏ预编译的 Java cd建的。当Ӟ此类外观的可自定义性要差一些;只能Ҏ式表信息和包含的囑փq行修改。不q,如果性能是您要考虑的最重要的因素的话,应该考虑为页面上特定的元素或特定 Portlet 启用高性能外观。(请参?a >参考资?/font>中的信息中心以了解详l信息,包括各种可帮助您~写高速外观和主题的提C。) ?WebSphere Portal 应用E序开发和验证的所有阶D均可以使用各种工具提供帮助。本部分对不同开发周期中可以使用的不同工L别进行了说明Qƈ提供了一些例子,以帮助您q行自定义代码的开发和分析?/p>
从技术角度而言Q可以用Q何文本编辑器~写 Portlet、主题和外观Q但使用集成开发环境(如将 IBM Rational] Application Developer ?IBM Portal Toolkit l合使用Q要方便很多。还可以使用 Portlet 代码CZ和基本门户代码片断开速入门;该开发环境还与一个门h务器q行了集成,以便立即部v和测试代码?/p>
当代码就l,可以部vӞ需要详l了解其可能的性能问题。可以采取若q步骤(下面Ҏq行了ȝQ,但性能斚w有一条始l适用的一般规则:在大多数E序中,U有 80% 的执行时间都花在 20% 的应用程序代码中。这 20% 的代码位于“关键\径”上Q正是这些方面值得q行性能优化。例如,Portlet 的呈现方法要比其初始Ҏ的性能关键性更强,因ؓ每个h都会调用呈现Ҏ?/p>
代码分析应在开发的早期阶段q行Q或其作ؓ开发后的第一个性能试。分析意味着在ҎU收集执行时信息Q通常会?JVMPI 接口q行此项工作。分析器l果可以帮助标识应用E序的关键\径;卛_部分旉所执行的代码。分析器q通常会给出关于对象创建速率和内存用的信息?/p>
IBM Rational Application Developer
提供了一个分析代理,可以使用该代理测试程序?/p>
Eclipse Test and Performance Tools Platform
是一?Eclipse 目Q提供了Z Eclipse 的开放源代码分析功能支持?/p>
一旦将 Portlet 部vC门户中,应该测?Portlet 在负载下的行为。压力或负蝲生成器(?Rational Performance Tester、Rational Robot、Apache JMeter {等Q是h成本效益的负载测试解x案,可以帮助您准地模拟生负蝲下的pȝ性能。这些工具将攉大量信息Q以帮助定pȝ是否h良好的性能设计Q其中包括关于请求响应时间、处理器使用率等的数据?/p>
在负载测试期_应该监视门户环境中的若干性能参数。IBM Tivoli] Performance ViewerQ与 WebSphere Application Server 一h供)可以帮助监视应用E序服务器内的资源用情c?/p>
门户环境的许多问题都和内存有兟뀂JVM 实现为工h供了两类信息Q以供进行性能分析Q?/p>
?IBM alphaWorks 中可以得到垃圑֛收器输出的分析工兗而另一斚wQheapRoots 则是一Ƒּ大的堆{储分析辅助工兗《IBM Java 诊断指南》也提供了处理门L相关性能问题的有用信息。请参阅参考资?/font>Q以获得q些参考资料的链接?/p>
开?WebSphere Portal 代码Ӟ通常不需要所有这些工P但要在生产环境中推出更大的门P必须从性能的角度对门户代码有个良好的理解?br /> 创徏自定义门户代码时Q开发h员必考虑很多斚w的因素,以确保门h能得到优化。小l如下: 精力主要放在关键代码\径的改进上。关键代码\径是处理旉长或频繁执行的代码\径。找到哪些类的哪些方法位于关键\径上。在关键路径外的优化效果相当?/p>
要同时兼执行性能和内存分配?/p>
使用恰当的工h量和分析代码Q以获得最典型的用户交互?/p>
不同~码问题解决Ҏ可能有很大的性能变化?/p>
必须全面了解处理发现的性能问题的特定实现的l节?/p>
设计自定义代码时要考虑后端讉K模式?/p>
不要错误地将会话作ؓ Portlet 的通用数据存储Z用。可以采用更好地Ҏ处理数据Q以满各种不同的实现要求?/p>
考虑利用 WebSphere Application Server ?WebSphere Portal 提供的特D功能以优化 Portlet 性能Q假讄标环境也在用相同的功能Q?/p>
StringBuffer sb = new StringBuffer("Hello ");
sb.append(var).append(" World");
String s = "Hello" + var + " World";
String logData = "Parameter 1: " + param1;
if (logger.isLogging(DEBUG)) {
logger.log(logData);
}
Mail mail = myCache.get("myMail");
if (mail == null) {
mail = readMailInformation();
myCache.put("myMail", mail) ;
}
...
PooledObject po = myPool.get();
...
// use the PooledObject
...
myPool.put(po);
. . .
public class IDontCare extends GenericPortlet {
private javax.sql.DataSource ds;
public void init() throws javax.portlet.PortletException {
try {
Hashtable env = new Hashtable();
env.put( Context.INITIAL_CONTEXT_FACTORY,
"com.ibm.ejs.ns.jndi.CNInitialContextFactory" );
Context ctx = new InitialContext( env );
ds = (javax.sql.DataSource)ctx.lookup( "jdbc/MYSHOES" );
ctx.close();
} catch (Exception any) {
// handle exceptions here
. . .
}
}
. . .
public void processAction (
ActionRequest request,
ActionResponse response
) throws PortletException, IOException {
. . .
try {
Connection con = null;
ResultSet rs = null;
PreparedStatement pStmt = null;
con = ds.getConnection ( dbuser, dbpasswd );
pStmt = con.prepareStatement(
"select * from myscheme.size_of_shoes");
rs = pStmt.executeQuery();
. . .
// release the resource when it is no longer used
if (pStmt != null) pStmt.close();
if (con != null) con.close();
} catch (Exception any) {
// handle exception here
. . .
}
}
}
<%@ include file="include2.jsp" %>
<jsp:include page="include2.jsp" flush="true" %>
javax.servlet.RequestDispatcher
static final boolean LOGGING = false;
if (LOGGING) {...}
List list = Collections.sychronizedList(new ArrayList());
<@ page session="false"%>
PortletRequest.getPortletSession(false)
<expiration-cache>300</expiration-cache>
renderResponse.setProperty(
RenderResponse.EXPIRATION_CACHE,
String.valueOf(numberCrunchingCalculation())
);
. . .
import com.ibm.wps.util.RemoteCacheInfo;
import javax.portlet.RenderResponse;
. . .
/* Do rendering */
public void doView(RenderRequest renderRequest, RenderResponse renderResponse)
throws PortletException, IOException {
/* Some code might happen here */
. . .
/* Publish a dynamic expiration time during rendering */
renderResponse.setProperty(
RenderResponse.EXPIRATION_CACHE,
String.valueOf(numberCrunchingCalculation())
);
/* Publish a cache scope value of "shared" during rendering *)
renderResponse.setProperty(
RemoteCacheInfo.KEY_SCOPE, RemoteCacheInfo.Scope.SHARED_STRING );
/* Some other code might happen here */
. . .
}
<!-- Theme "shared" scope and 40 seconds cache expiration -->
<theme action="update" active="true" objectid="xmplTheme" uniquename="wps.theme.example">
<parameter name="remote-cache-scope" type="string" update="set">SHARED</parameter>
<parameter name="remote-cache-expiry" type="string" update="set">40</parameter>
</theme>
]]>