级别: 初级 Bill Hines, 认证顾问 I/T 专家, IBM Software Services for WebSphere
2004 年 7 月 01 日 本书的摘录说明了如何在 WebSphere Application Server 中使用不同类型的高速缓存来尽可能地提高分布式 Web 应用程序中每一层的性能并将其工作负载降到最低。
本文摘自一本新书“
IBM WebSphere: Deployment and Advanced Configuration”,该书由 Roland Barcia、Bill Hines、Tom Alcott 和 Keys Botzum 编著,定于 2004 年 8 月由 IBM Press 出版。
引言
高速缓存是提高应用软件性能的一项重要技术。几十年来,开发人员已经在许多技术中人工加入高速缓存。J2EE 和其他应用程序一样受益于高速缓存技术。WebSphere Application Server 通过引入一种功能强大的称为动态高速缓存 (dynacache) 的机制来识别这种技术。
当考虑在 J2EE 应用程序中使用高速缓存的时机时,研究一下典型应用程序的基本体系结构是有帮助的。J2EE 应用程序开发人员通常按照模型-视图-控制器 (MVC) 的范例来开发自己的应用程序,在这种范例中,有“多层”代码来处理用户界面(视图)、业务逻辑(控制器)和数据访问(模型)。 同样地,支持该应用程序的物理拓补结构也分为多层:
- 最顶层加载负载平衡器和高速缓存代理。
- Web 层包括 Web 服务器。
- 应用程序层包括一个像 WebSphere Application Server 这样的应用程序服务器。
- 数据层可能包括数据库服务器或企业信息系统 (EIS)。
在这些分布式体系结构中,高速缓存可以用来尽可能地提高每一层的性能并将其工作负载降到最低,最终达到提高性能的目的。
通常高速缓存分为两类:一类是静态高速缓存,也就是说缓存中的内容是静态的、不变的。例如 HTML 文件,图象文件或 JavaScript 文件;另一类是动态高速缓存,保存程序动态运行的结果。在这一章里,我们将以一个基本的多层次应用程序体系结构为例,探讨高速缓存可以应用在哪些地方?然后我们将详细讨论不同高速缓存的各种参数,以及如何在应用程序中识别高速缓存潜在的作用。
使用高速缓存的时机
下面的图 1 显示了一个典型的没有使用高速缓存的 J2EE 请求/响应实现流程。为了清楚地向读者介绍高速缓存的优点以及在配置了各种高速缓存后对一个请求/响应的影响,我们在这一章中将始终要用到这张图。每次执行一个动态请求时,比如请求一个 servlet 或 JSP,它可能需要遍历整个流程,如下图所示。
即使是一个简单的带文件处理的静态请求也需要通过应用服务器来实现。显然地,为了完成这些请求,在不同的服务器之间有很多的网络跳转并且在各个服务器上都有资源正在被使用。网络跳转会给系统带来网络等待。更重要的是,每一层的处理都需要花费时间和资源。例如在应用程序服务器层,每一个跳转都会影响到很多层,如 Web 层、EJB 层、还有其他的服务如 JDBC 和 JNDI 等。可以想象一下,只生成一个简单的页面(可能是通过 JSP 来实现),必须执行一个 servlet、调用 EJB、访问终端数据库,然后将整个结果生成一个显示页面。不用想就知道当有相同的结果返回的时候会重复执行这个过程。比如有成百上千个用户想看同一个产品的目录介绍,就会导致工作负载过重和资源的严重消耗,而这原本是可以避免的。高速缓存就是提高性能的一项关键技术。
图 1 是一个简化了的图,通常为了故障转移和可扩展性,在每一层都有很多服务器。如为了满足代理服务器的需要,Web 层可能由很多前端加载了负载平衡器的 Web 服务器组成。为了显示“最坏”的场景,我们在图中包括了一个代理服务器。因为我们后面还有讨论这个问题,所以我们在这个情景中包含了尽可能多的网络跳转。但是实际上很少有需要使用代理服务器的情况。应该注意到,如果一个代理服务器实际上被当作高速缓存代理服务器使用,它会自动提高性能,在后面的关于 WebSphere Application Server 高速缓存代理服务器的讨论中您会看到这一点。但同时在应用服务器和
用户目录服务器上会产生一些冗余信息。
图 1. 没有使用高速缓存的 J2EE 请求/响应
高速缓存在性能上的相互关联
在图 1 的场景中,假设有一个用户请求某产品的目录,不难想象,服务器会调用各种各样的资源来完成用户的请求。为了构建产品列表,服务器会执行复杂的 SQL 联合查询语句从产品和市场数据库中提取数据,就像 Siebel 为了满足客户对价格和信息的需求而调用 ERP 系统一样。如果这个请求不是通过调用终端服务器来实现的,而是仅仅从前端代理服务器上的高速缓存中返回 HTML 结果,用户就会考虑性能上的差别。另外,当大量的请求在高速缓存中被解析,这些终端服务器的工作负载将大大减少(与消耗资源相对)。结果是吞吐量越大,现有的硬件和软件投资所能服务的用户就越多。不难看出,高速缓存是一个主要的性能调谐钮。在某些情况下,它能使系统性能提高三倍。
我们经常说由于应用程序编码不规范导致用户不能调整其运行。但是可以通过 WebSphere Application Server 性能优化在一定程度上减少糟糕的代码对性能的影响。比如调整线程和连接池中的网络队列的排列,计算 JVM 堆栈大小。通常,提高应用程序性能最有效的办法是从根本上避免任何操作。动态高速缓存在这方面提供一些解决办法,特别是针对那些在重负荷下停止运行的应用程序。动态高速缓存直接由 WebSphere Application Server 提供支持,所以应用程序与其建立“自制的(home-grown)”高速缓存子系统(比如安全性、持续性以及其他类型的系统代码),不如利用由 IBM 提供的信息基础设施。一般说来,应用程序应该把注意力集中在如何解决当前业务问题的逻辑处理上,而避免开发自定义的高速缓存代码。
高速缓存静态文件
最简单的高速缓存形式是静态文件内容的高速缓存。在这一小节里,我们将讲述在 WebSphere Application Server 里怎样处理静态文件,以及何时缓存静态文件。
静态文件处理
为了理解在 WebSphere Application Server 里如何处理静态文件,我们先以 J2EE 应用程序为例进行说明。回顾 J2EE 应用程序被打包为一个 EAR 文件,EAR 文件又包含多个 J2EE 模块,还包括一个 WAR 文件。WAR 文件包含 Web 相关组件、静态元素(如 HTML 文件、图像、servlet、JSP 文件)或其他 Java 类。在最简单的部署场景中,EAR 文件被部署到一个应用程序服务器之后,就开始处理整个应用程序。包括应用程序的静态内容,该静态内容由一个叫做 File Serving Enabler 的 WebSphere Application Server 工具进行处理。
File Serving Enabler 是应用程序服务端 Web 容器中的一个组件,它监听可能成为文件请求的 HTTP 请求。File Serving Enabler 就像一个 Web 服务器,处理静态文件。基本上任何与 servlet 或 JSP URL 不匹配的请求都被当作一个文件名,文件服务组件从 WAR 对该名称进行处理。当一个应用程序被部署到 WebSphere Application Server,其中一个步骤是重新生成插件文件并将其复制到 Web 服务器上。重新生成插件之后,会创建充当“通行”规则的条目,它告诉 Web 服务器始终将某些请求向前转发到 WebSphere Application Server,而尽量不要自己处理文件。当 File Serving Enabler 打开后,插件文件的条目可以是根据上下文得到的任何文件,如下面的样本 1 中的代码所示。
样本 1. 带有已启用的文件服务的 plugin-cfg.xml 条目
在应用程序服务器工具包 (ASTK) 不选中 Web 模块这个选项或者修改
ibm-web-ext.xmi 文件,可以关闭 File Serving Enabler。当它被关闭之后,针对这个 Web 模块的插件条目会发生很大的变化,它包括只针对动态内容的通行规则,如下面的样本 2 所示。
样本 2. 带有已禁用的文件服务的 plugin-cfg.xml 条目
<Uri Name="/pts/PTSMainServlet"/>
<Uri Name="/pts/*.jsp"/>
<Uri Name="/pts/*.jsv"/>
<Uri Name="/pts/*.jsw"/>
<Uri Name="/pts/j_security_check"/>
|
考虑一个 Web 请求,它是从浏览器发出的,请求一个简单的 HTML 页,该页面带有我们的 J2EE 应用程序中的一些内嵌图形,我们的 J2EE 应用程序已经如上进行了部署,并且启用了 WebSphere Application Server 文件服务。Web 服务器会监测到该请求并在插件文件中发现通行规则,从而相应地将该请求转发到 WebSphere Application Server。在 Web 容器里,File Serving Enabler 从部署应用程序的目录中读取文件并返回请求。当用户的浏览器接收到 HTML 文件时,它会解析该文件并将结果显示出来。在这个过程中,浏览器会发现一些需要用来呈现页面的图形文件,并且会为这些文件发送请求,File Serving Enabler 同样会处理这些请求。
用户可能会想,虽然这是最简单的部署场景,但在性能上效率并不是最高的。在 WebSphere Application Server V5 之前,部署人员经常把静态内容和 Web 服务器分开,使得 WebSphere Application Server File Serving Enabler 也无能为力。毕竟,Web 服务器是被设计用来处理静态内容的,在这方面处理速度毫无疑问要比应用服务器快。静态内容的数量越多,处理起来就越快。但是,这也加重了部署的复杂性,因为现在内容必须既部署在 Web 服务器上又要部署在应用服务器上。同时它也使打包的过程也变得复杂化了,因为我们再也不只是部署一个简单的 WAR 文件了。(请参见
参考资料以获得与这种方法有关的技术和折衷方案)。从本质上来说,我们需要在打包的简易性和通过高速缓存提高性能的必要性之间进行权衡对比,幸运的是,WebSphere Application Server V5 的 Web 服务器插件很好地解决了这个问题。通过配置 Web 服务器插件,可以在 Web 服务器端高速缓存静态内容,而且打包之后仍然是 WAR 文件。
浏览器高速缓存
对于大多数 Web 用户来说,他们最熟悉的高速缓存是浏览器高速缓存。Web 浏览器从 Web 请求检索文件。例如图形文件,并把他们作为临时网络文件缓存在用户的硬盘上,然后在那里对他们进行处理,调往下一个请求。这样做有两个好处:一是用户可以更加快速地获取文件,二是 Web 服务器可以闲置下来为其他的用户提供更多的服务。但是,只有当该用户的硬盘被作为缓存,这种文件缓存拷贝才对他起作用。由于高速缓存能使很多用户受益,我们应该也在系统体系的其他层缓存一些文件。比如,假设有 1000 个请求带有一张大小为 100 千字节的图像的页面,如果只在浏览器端建立高速缓存,前 1000 个用户点击该页面会造成应用服务器上从浏览器到文件处理过程之间的端到端的请求与响应,还要将结果反馈给用户,这样就会导致 WebSphere Application Server 需要处理 100MB 的冗余数据。然而,像我们将在本章中所看到的那样,充分利用多种高速缓存,将第一个用户请求的文件保存在高速缓存里,这样对其他用户来说,从高速缓存中读取文件会更快捷,同时也节省了检索其余 999 个请求所需要消耗的资源。WebSphere Application Server 只需要处理一个大小为 100千字节的请求就可以完成同样的工作负载。
Web 服务器高速缓存
大多数 Web 服务器都可以缓存静态文件。WebSphere Application Server 附带的 IBM HTTP Server 有一个在 AIX 上叫做 Fast Response Cache Accelerator (FRCA) 而在 Windows 上叫做 Adaptive Fast-Path Architecture (AFPA) 的设备,它既能缓存静态内容(两个平台上均可)又能缓存动态内容(只适用于 Windows)。它是一个基于内核的缓存,在我们目前所讨论的缓存中速度是最快的。但是,静态缓存只限于缓存 HTTP server 自身处理的文件,而不能缓存那些通过通行规则把请求转交给应用服务器之后被调用的文件。为了把这种缓存应用到我们的场景中,我们与其把静态文件部署到 Web 服务器上将他们分开保存,不如使用一个简易的部署以及 WebSphere Application Server 内置的文件处理设备。下面的图 2 显示了从 Web 服务器处理一个请求的流程。
图 2.从 Web 服务器静态缓存中处理文件的请求/响应流程
WebSphere Application Server 插件静态高速缓存
对于 WebSphere Application Server 来说,在 Web 层实现高速缓存的另一个办法是用 WebSphere Application Server 插件缓存静态文件。WebSphere Application Server V5 插件和 Edge Side Includes (ESI) 都有高速缓存和装配页面以及页面片段的功能(包括静态和动态的)。ESI 是 IBM 、Akamai 和其他一些公司为页/片段高速缓存及其装配编写的一个开放标准。用标准 ESI 标记语言编写的片段最后可以组装到一起,但是高速缓存不需要特殊的标记语言。比如 IBM 的 WebSphere Edge Server、 WebSphere Application Server V5 Network Deployment 以及 Akamai EdgeSuite 都遵从 ESI 标准。(有关 ESI 的更多信息,请访问
http://www.esi.org。)
下面我们将主要集中讨论插件的作用,使用 ESI 在装有插件的 Web 服务器上缓存静态内容。参考上面的网络拓扑结构图,在上面的图 2 中可以看到怎样使用 ESI 在网络跳转中检索静态缓存文件,这基本上和从 Web 服务器上的静态缓存中检索文件类似。但是,使用插件的好处在于静态内容可以保存在 WAR 文件里,这样可以简化打包的过程。ESI 也可以缓存动态请求,这一点稍后再作讨论。插件里的高速缓存可以减少后端应用服务器上文件处理设备的负担。这和将静态内容分开保存在 Web 服务器上不同,但对于那些不愿意执行额外的部署步骤的企业来说这样可以少做一些管理方面的工作。
下面的样本 3 是 plugin-cfg.xml 文件中的一些条目,该文件是 ESI 的配置文件。默认设置为:ESI 处于工作状态,Web 服务器端插件正在运行,缓存大小被设置为 1024千字节(1MB)。当这个空间满了的时候,按照到期的紧急程度从缓存中清除条目(最先到期的条目最先被清除)。通过设置失效监控与 WebSphere Application Server 交互,检测缓存区中的某一个或一组条目是否无效,是否应该被清除。同时它也监视在静态缓存中有哪些条目,以便于统计。
<Property Name="ESIEnable" Value="true"/>
<Property Name="ESIMaxCacheSize" Value="1024"/>
<Property Name="ESIInvalidationMonitor" Value="false"/> |
默认情况下,在 ESI 中,静态缓存中的条目每 300 秒(5 分钟)之后就会被清除。如下面的样本 4 所示,这个时间可以通过在应用服务器上的 JVM 命令行参数中设置属性进行修改。时间单位是按秒来计算的,在样本 4 中设定了一个两分钟的超时。
-Dcom.ibm.servlet.file.esi.timeOut=120 |
WebSphere Application Server 附带的两个应用程序也被用来作为高速缓存。可以在
应用程序安装 目录中找到这两个程序,下面就对这两个程序作一个介绍。
|
Cachemonitor.ear
| 允许监控插件中的 ESI 缓存和应用服务器上的动态缓存,进行一些基本的操作,比如清空缓存区。 | |
DynaCacheESI.ear
| 由一个 servlet 组成,当它被安装在应用服务器上的时候充当外部缓存适配器。可以使应用服务器缓存引擎将缓存信息集中起来,以在缓存监控应用程序上显示,并且可以给插件中的 ESI 缓存发送缓存文件以及相关的信息。 |
默认情况下,插件可以缓存静态文件,但是如果没有安装 DynaCacheESI 适配器,也就意味着静态缓存文件要到被设置的时间限制之后才会被清除。通过安装 DynaCacheESI 适配器,更新静态文件的时候,WebSphere Application Server 会给插件发送设置失效消息,同时,请求 Cachemonitor 程序统计缓存信息将结果显示给用户,并清空缓存区。
为了运行这两个应用程序,需要将
plugin-cfg.xml (参见样本 3)上的 ESIInvalidationMonitor 属性设置为 true。首先安装这两个应用程序,重新生成插件,再重启应用程序服务器。运行一个实例程序会产生缓存条目。图 3 显示了缓存监控应用程序的一些 ESI 缓存统计信息,图 4 显示了一些样本缓存内容。请注意,监控应用程序自身也缓存图形文件和 JavaScript 脚本文件,比如 PetStore 这个演示应用程序。它们不需要额外配置就可以缓存静态文件。
图 3. 来自 WebSphere Application Server CacheMonitor 应用程序的 ESI 统计信息
图 4.WebSphere Application Server CacheMonitor 应用程序的 ESI 缓存内容
安全提示:在 WebSphere Application Server v5.0 和 5.1 中,某些情况下,一些未经授权的用户通过 WebSphere 安全基础设施不用身份验证就可以从 ESI 缓存中访问静态内容。在 WebSphere Application Server 技术支持网站上可以下载临时修复文件 PQ81192。
动态高速缓存
正如上面所描述的,随着应用程序中的静态元素在数量和容量上的日益增加,静态高速缓存越发显示出自己的优势了。但是,动态网站也越来越趋向于个性化,越来越开放,这使得动态高速缓存也越来越有用武之地了。除了上述原因之外,在构建一个响应时,动态请求往往会消耗更多的企业资源。在一个企业系统里,动态内容通常需要大量的资源密切的合作,所以动态高速缓存可以极大地提高性能。回到图 1,研究一下为了构建这些请求,每一层都必备的运算能力的总数。每一层 CPU 的使用、内存的使用以及层之间的通信都是很大的,特别是在应用服务器、Web 服务器和 EJB 容器中。找到后端数据库、主机、EIS 以及其他服务器往往也消耗很大。对于动态缓存中的每一个请求处理,使用这些资源是不必要的,它们可以用来处理更多的请求。应该优先考虑购买其他的服务器来处理最大负荷。
动态高速缓存比静态高速缓存更复杂,需要更详细地了解应用程序。从应用程序的角度来考虑,不同的应用程序动态生成的内容不尽相同,所以必须为动态高速缓存准备一个备用缓存。因此,研究在什么情况下缓存动态生成的内容并返回正确的响应是非常重要的。这需要知道应用程序的相关知识,如它可能存在的状态或一些其他的数据,比如确保在确定性的方式下能生成动态数据的参数等。
WebSphere Application Server 5 中的动态缓存可以通过管理控制台来进行管理。浏览整个管理控制台菜单,选择
Servers => Application Servers => server1,您可以看见 Additional Properties 下的 Dynamic Cache Service。单击该菜单会弹出一个如图 5 所示的页面。
图 5. WebSphere Application Server 管理控制台上的动态缓存服务设置
在这里可以看到几个可配置的动态高速缓存选项。需要特别注意的是,高速缓存的大小是缓存条目的数量,而不是在探讨插件 ESI 高速缓存工具时我们所看到的物理存储量的大小。这样更便于测量高速缓存的大小;可以通过监测高速缓存统计表,在负载测试和生产周期峰值期间,来监管高速缓存的驱逐。这里显示的默认优先级设置是与驱逐(eviction)相联系的。是否驱逐通过 LRU(Least Recently Used) 算法来决定。优先权实质上是一个高速缓存条目,当从 LRU 算法中得出它应该失效时,它能够自由通过并继续驻留在高速缓存中的数字。这项设置很少需要改变,但是当配置单独的缓存条目属性时应当记住这一点。设置较高的数字会延长他们在高速缓存中的驻留时间,所以您可能会为那些生成开销大的条目设置较高的数字。您可能也注意到了,这里有一个工具,可以将在缓存区满的时候将高速缓存条目卸载到磁盘上,而不是把它们从缓存区彻底地清除。虽然在磁盘卸载区页面的响应时间比高速缓存区慢很多,但是这样做通常优于让高速缓存失效,因为那样做的结果是页面不得不通过运行事务进行全面的重建。
为了使用动态高速缓存,Web 容器必须启用 servlet 高速缓存。浏览整个管理控制台菜单,选择
Servers => Application Servers => server1 => Web Container,会弹出如图 6 所示的带有选项的页面。注意启用 servlet 高速缓存的选项被选中了。
图 6. 启用 servlet 缓存的 Web 容器设置
动态高速缓存选项
下面介绍 WebSphere Application Server 动态缓存服务的一些特性。所有这些服务是由 WebSphere Application Server 里的同一个高速缓存引擎提供的,因此它们的配置是类似的。接下来将详细地讨论每一个特性。
|
Servlet/JSP 缓存
| servlet/JSP 高速缓存设备通过调用 servlet/JSP 捕获响应信息并缓存 HTML 结果。通过输入文件
cachespec.xml (下面有详细描述)Servlet 被配置为高速缓存。该文件可以通过 URI 路径或类名指定。实际上通过类名指定文件使用得更广泛,因为它会捕获任何 servlet 调用而不考虑别名。通常,由于不同的别名代表不同的操作,servlet 常以它的别名缓存。是按照 URI 还是按照类名缓存完全取决于应用程序。大多数情况下,进入 servlet 缓存中的文件需要进一步被其他的一些输入进行验证,例如必选参数或者是用户的会话信息。我们将在这一节继续讲述怎样指定缓存文件。
准备执行一个 servlet 时,动态高速缓存引擎会过滤掉即将被执行的 servlet 中的 service() 方法,看它是否与当前的参数中输入的缓存 ID 相匹配。如果匹配,与其返回缓存结果不如直接执行 service() 方法,然后再进行其他的工作。这样就可以避免本来需要 servlet 来完成的所有的处理过程,进而提高性能。如果不匹配,仍旧执行 service() 方法,在结果返回给用户之前被捕获并保存在缓存中。
| |
命令缓存
| 命令缓存可以缓存调用服务器端的命令的执行结果,实现 WebSphere 命令模式接口。这对高速缓存密集型操作(如运行复杂的联合 SQL 语句或主机请求)很有好处。命令缓存的结果通常是一个对象或对象容器,而不像 servlet/JSP 缓存是 HTML 页。如果用户能从命令模式的高速缓存中受益,广泛使用该模式,会比翻新一个已有的应用程序相对简单。翻新包括在现有的代码周围写命令。与 servlet 或 JSP 的 service()方法缓存引擎过滤器类似,命令缓存对 execute() 方法进行过滤来决定命令是否可以缓存以及缓存中是否已经有内容。 | |
Web 服务缓存
| 这种缓存包括 Web 服务 SOAP 调用的结果。Web 服务 SOAP 调用可能需要消耗较多的资源,主要是由于前端和后端都需要大量地解析 XML 文件。缓存标识符既可以从 HTTP 头文件建立又可以从 SOAP 信封建立,事实上,可以散列整个 SOAP 信封,作为一个缓存 ID。(更多的相关信息请参阅 WebSphere Application Server V5 Information Center。) | |
分布式对象缓存
| 这种缓存主要包括用在分布式环境中的 JAVA 对象。例如,对象可以存放在应用服务器上,供其他的应用服务器在同一个数据复制服务(DRS)集中检索。但是,这种缓存只适用于企业版的 WebSphere Application Server。配置在缓存实例资源(和 JDBC 资源类似)上的 JNDI 检索这些缓存的实例。当服务器停止运行,加载新的任务准备重启时,如果对象是连续的被刷新到硬盘,可以配置该缓存。个别的条目可以不设为共享,推(当它们被缓存的时候被发送到所有的服务器上),或者拉(仅发送他们的名字,只有当从其他服务器上“被拉”时才检索其值)。(要获得更多的有关使用 DistributedMap 界面进行动态缓存的信息,请参阅 WebSphere Application Server V5 Enterprise Information Center。) |
安全提示:动态高速缓存不是被设计为维护安全敏感的信息的。虽然使用 DRS 加密可以保护信息使其免受 WebSphere Application Server 环境之外的攻击,但是没有保护 WebSphere Application Server 内部信息的方法。因此,任何应用程序可以从同一个应用服务器内部访问任何缓存信息(servlet缓存、对象缓存、Web服务缓存或者命令缓存)。如果用户不信任同一个应用服务器内部的应用程序,就不要在缓存中保存敏感信息。幸运的是,虽然高速缓存通常用来缓存共享信息,但这个限定不是一个主要问题。
动态高速缓存相关概念
设置失效
我们可以发现,对于静态文件,设置失效非常简单。HTML 文件通常在文件头部有到期标记,其他类型的文件也有一个简单的超时配置。但是,对于动态文件,设置失效比较复杂。动态缓存有下列设置失效的方法。
|
简单的超时
| 缓存规范上规定,对每一个缓存条目,用户可以指定一个默认的超时范围。这个时间可短(用秒来计算)可长(用年来计算)。甚至也可以无限长。 | |
动态设置失效
| 假设您正在缓存像某个棒球队本赛季的日程安排表这样的长期数据。在这种情况下,将本赛季末,比赛结束之前设为超时比较合理。但是,如果在这期间下雨,则一切都会被打乱。可以使用动态失效处理这种情况。利用被指定为“仅仅失效”(参考下一节关于创建高速缓存规范)的缓存条目或者通过动态高速缓存提供的 API 函数来实现。在刚才的例子中,用户也可以为更新棒球队日程安排表的 servlet 配置一个设置失效缓存条目。这样当动态高速缓存引擎监测到该 servlet 在运行时,它就会将保存了该球队日程安排的缓存条目设置为无效。开发人员可以直接在代码中应用一个设置失效的 API 函数,虽然不提倡这样做。简便起见,应用程序应该脱离开发环境运行。 | |
驱逐缓存条目
| 当缓存区满了的时候,缓存条目被设置为无效,然后被清除来缓存新的条目。对于静态高速缓存,可以按其超时时间来简单计算应该先清除哪个条目。对于动态高速缓存,通过 LRU(Least Recently Used) 算法来决定,也就是给每个缓存条目指定一个优先级(参见前面的关于这方面的探讨)。 |
高速缓存条目详解
高速缓存条目在
cachespec.xml 这个文件中有详细的设置。这个文件包含了高速缓存设置和失效策略。可以将它放置在
<was-root>/properties 目录下作为全局高速缓存设置(这个目录下已经有了一个示例文件,将它名称改为
cachespec.xml 它将对 snoop sample servlet 应用高速缓存),或许您更希望为每一应用程序模块单独定义该文件,这样就需要把它们放置在每一 Web 模块的 WEB-INF 目录下,或者是放置在 enterprise bean 目录下的 META-INF 文件夹里。应用程序服务器会按照设定的时间间隔重新加载这些文件。下面是
cachespec.xml 文件的例子,JSP(样本 5)、命令(样本 6)和 Web 服务调用(样本 7)。在每一个样本的下面都会根据
cachespec.xml 文件中对高速缓存条目的总体描述加以论述。(有关详细的应用和配置高速缓存策略的更多信息,请参阅 WebSphere Application Server Information Center,或者到应用程序服务器 properties 目录下查看
cachespec.dtd 文件。)
cachespec.xml 文件的构成
<cache>
|
cachespec.xml 文件的根元素只出现一次。该文件包括多个
<cache-entry> 节点。
|
<cache-entry>
| 一个该节点对应一个高速缓存的项目。高速缓存条目用来描述被缓存的项目,这些项会使其他的一些高速缓存条目无效,也可能使高速缓存条目之间互相依赖。我们将在下面分别看到这两种情况的样本。 |
<class>
| 确定条目的类型,可能的值是“command”、“servlet”或“webservice”。 |
<name>
| 高速缓存的项目的名称。对 command 而言,应该是充分合格的包和类的名称,包括 .class 后缀。对 servlet 和 JSP 而言,是相对于应用程序上下文的 URI 路径。例如 JSP 文件的 URL 为
http://www.myco.com/myapp/products/catalogList.JSP ,那么它的值就是
/products/catalogList.JSP 。如果使用的是应用程序服务器 properties 目录(全局)中的
cachespec.xml 文件,就需要一个完整的 URL 路径。如果有多个 servlet 别名,那么将放置多个
<name> 来列举每一个要应用高速缓存的别名。
|
<property>
| 用来设置高数缓存条目的可选属性,比如是否可以缓存在 WebSphere Application Server 之外或者是否可以保存在磁盘上。(WebSphere Application Server V5 Information Center 中有一个定义好的属性列表。)一个高速缓存条目可以具有多个属性。 |
<sharing-policy>
| 决定一个高速缓存条目是否在分布式高速缓存之间公享,如果是的话则描述如何共享。下面将讨论分布式高速缓存。 |
<cache-id>
| 每一条高速缓存都需要设置标识符。这是一个键值,如同数据库里的键结构,唯一地标识每一个高速缓存条目。高速缓存通常并不是简单地指定某一个 servlet、JSP 和 command 来加以缓存。而是必须具备一些合格的属性,以区分不同的条目。例如,通过 URI
/weather/forecast 高速缓存一个 servlet 返回天气预报就不会令人满意;想一想如果一个用户需要 Miami 的信息,这些信息已经应用了高速缓存,将会出现怎样的情况,而下一个用户又通过调用 URI 想得到 Anchorage 的天气预报。既然这样,这里可能就需要一个随同 servlet invocation 一起发送的请求参数,可能是要获取的天气预报的城市名或是邮编区号。因此这个参数确保了高速缓存返回的结果是唯一的,也是用户所请求的,例如 Miami 的天气预报就可以提供给其他需要它的用户。这个参数应该设计到请求中,因为没有它,高速缓存条目是无意义的。样本 5 显示了一个这样的高速缓存规范。在 cache ID 可能会有其他的一些参数,比如用户需要的是短期的还是长期的天气预报。如下面的例子所显示的,cache ID 的每一组成部分都用
<component> 标记来指定。
|
对如何通过技术手段来得到 cache ID 不要感到惊讶。Servlet/JSP 的 cache ID 可能会是请求的参数/属性、路径信息、 header 值、请求区域或 cookie 值,甚至可能是 HTTP session 值。最好避免使用服务器端的值,比如 HTTP session 数据,因为那些使用者可能没有把高速缓存条目移到应用程序服务器的外层(因此就无法将其标记为 edge-cacheable )。我们将在下面探讨边缘高速缓存。对基于 Struts 程序设计模式的应用程序而言,使用路径信息是很有效的,因为 servlet names/aliases 是动态的,所以只有
.do 后缀可以表示它们。使用 controller servlets 的应用程序,如果它的参数有一个值的“列表”,同一个 servlet 可能会多次标记为应用高速缓存,而且当操作参数有“更新”值时会标记为无效。command 的 cache ID 可以基于一个 command object 的方法,Web 服务的 cache ID 可以基于 SOAP 请求的信息。还没有提到的是当使用 Distributed Object Map 时,用程序来指定 cache ID 。一个高速缓存条目可能有多个 cache ID 。我们已经熟悉了
cachespec.xml 文件的基本结构,现在来看几个样本。
样本 5. JSP 缓存文件实例 cachespec.xml
<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>
|
上面的样本 5 显示了只有一个简单的 JSP 缓存条目的文件
cachespec.xml 。您可能注意到类型是 servlet ,名称却清楚地指定为 JSP。这是因为所有的 JSP 在后台都被编译为 servlet,所以从应用服务器的角度看,实质上是同一概念。JSP 有一个“edge cacheable”状态属性。(将在后面讨论在网络边缘应用高速缓存。)注意 cache ID 只有一个参数,就是请求的天气预报城市的邮编区号。这就意味着如果七个用户通过七个不同的邮编区号发出请求,就会有七个以邮编区号做为键值的高速缓存条目。以后的用户可以任意地对这七个邮编区号发出请求,都将得到他们所请求地区的天气预报。缓存条目的优先级是 3,意味着如果 LRU 算法将其指定为失效,它有可能“自由通过”三次,如同前面探讨失效时所述的。这个条目过期时间的默认设置是 20 秒;但是可以通过 API 函数指定另一个高速缓存条目或清除整个缓存区使其提前失效。
样本 6. 命令缓存文件样本 cachespec.xml
<cache>
<cache-entry>
<class>command</class>
<sharing-policy>not-shared</sharing-policy>
<name>com.myco.productapp.ProductListCommand.class</name>
<sharing-policy>not-shared</sharing-policy>
<cache-id>
<component type="method" id="getProductCategory">
<required>true</required>
</component>
<priority>1</priority>
<timeout>3600</timeout>
</cache-id>
</cache-entry>
</cache>
|
样本 6 是只有一个简单的命令高速缓存条目的文件
cachespec.xml 。
<class> 就是“
command ”。名称是全限定的包和类的名称。由
<sharing-policy> 的指定可以看出,它不会在分布式高速缓存中共享。cache ID 是调用 ProductListCommand 对象的 getProductCategory() 方法返回的结果。这意味着产品条目列表会通过类别进行排序,例如运动品、服装或电脑硬件。如果看到的产品列表一类的事物,产品类别改变的不是很快,所以这个高速缓存的过期时间为一小时(3600 秒)。
样本 7. Web 服务调用高速缓存的文件样本 cachespec.xml
<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>
|
样本 7 显示了只有一个简单的 Web 服务调用高速缓存条目的文件
cachespec.xml 。类别的指定是显而易见的,名称是 soaprouter servlet 的 URI 路径,这对 Web 服务的开发人员来说是很熟悉的。这个缓存条目配置有两个 cache ID 。一个是 stock quote lookup 请求和 SOAP 信封的组合,另一个是 getQuote 服务操作和 SOAP 信封的组合。在运行时,不管是向高速缓存中插入新的条目还是为请求返回一个条目,高速缓存引擎都会从上到下的分析这两个 cache ID,并且使用第一个与特定条目相匹配的 cache ID。
下面我们来看一个如样本 8 所示的更为复杂的
cachespec.xml 文件。
样本 8. Web 服务调用高速缓存的文件样本 cachespec.xml
<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>
|
样本 8 显示了只有一个 controller servlet 缓存条目的文件
cachespec.xml ,从 MVC-type 应用程序中可以找到这个文件。由上至下可以看到如下内容:
- 这个 cache ID 是在通过一个 product ID 和带有一个值为“view”的“action”参数运行 product controller servlet 时使用的。很可能是用来显示一件产品的信息。需要注意的是不仅要把参数名“action”作为 cache ID 的一部分,参数值“view”也要作为其中的一个组成部分。
- 这个 cache ID 是在提供一个种类参数和带有一个值为“view”的“action”参数运行 product controller servlet 时使用的。这是请求一个页面包含一个种类的多个产品的例子。
- 名称为“category”的 dependency ID,有一个“category”参数,值为 cache ID 。这个缓存条目和其他的缓存条目都可以有相同的 dependency ID ,并且可以在某些事件发生时作为一个组同时失效(比如一个失效规则被触发)。
- 被用来链接到 category dependency ID 的名称为“category”的失效规则有一个“action”参数,需要 servlet 提供的值为“update”才能够匹配。基于链接,当 servlet 以更新模式运行这 servlet,不论这个 category 高速缓存条目是单独的还是列表都会失效。
规划高速缓存
规划可以使高速缓存很好地得以应用,而不仅仅是对现有系统的翻新。成为后者是极有可能的,但是也并不容易。显然,最好在那些占用空间大、执行速度慢和生成开销大的操作(或文件)上应用高速缓存。它们也应该是公开的——应用的缓存条目使用者越多取得的效益越大。因此私有数据并不适合应用高速缓存。针对某一用户的个性化的页面也降低了应用高速缓存的可能性,但是对一个好的设计而言,将有很多的方法可以应用。设想一个航空公司的飞行常客页面,在欢迎信息里显示用户的名字、当前等级和积分,根据等级所提供的折扣,还有一些一般性的折扣信息和消息。如果这些在一个 JSP 文件里,那么就由于含有个人的特定信息而不适合进行高速缓存。然而,如果把这一页分离成独立的 JSP 片段,如下面的图 7 所示,这将使高速缓存发挥出巨大的潜力,特别是使用 ESI 在网络边缘利用和组织高速缓存。
图 7. 分成可缓存片段的样本页面
|
User.jsp
| 这一片段包含用户姓名、等级、当前积分和其他一些个人信息。它或许将不被缓存,因为只有一个用户能够从这个高速缓存中获益。 | |
Status.jsp
| 这一片段将包含级别(如 Platinum 、Gold 等等)、为这一级别提供的折扣和消息。高速缓存的条目很可能就是级别的名称,而且每一个级别可能单独的生成一个物理上的高速缓存条目,为这一级别的所有用户共享。 | |
Main.jsp
| 这一片段包含页面之间的通用信息:可能是通用的新闻、折扣、链接或查询。这一片段非常适合进行高速缓存。 |
现在介绍另一个普遍存在的在高速缓存过程中让人进退两难的问题,让我们假定有一个包含五个城市天气预报的片段。每一个用户都可以决定显示哪个城市(可能是他经常去的城市)的天气预报信息,他们的每一次访问都需要这样显示。因为必须与后端的计算机通信,所以重新提取天气信息的速度很慢。乍一看这里很适合使用高速缓存,毕竟天气预报信息是公共的、共享的数据。但是在他们的页面里显示相同五个城市的用户并不多,而且潜在的不同组合意味着物理高速缓存条目的数量巨大。一个可行的解决方案不是使用 servlet/JSP 缓存,而是利用命令缓存把所有主要城市的天气信息进行高速缓存。这样不论用户指定了哪几个城市,这些信息都能够从高速缓存中获取而不是来自速度缓慢的事务处理。如果可能被选中的城市不多,也可以将城市分成各自独立的片段,然后放入到包含五个城市的大的片段中。
将高速缓存向前端推移
高速缓存的一个强有力的特性是,一旦内容配置被为应用高速缓存,高速缓存会潜在地移向网络边缘,更加地靠近用户端。这样可以减少传输过程中网络跳转。例如,一般在应用服务器层处理高速缓存页片断,但是它可以被推向网络服务器层甚至是代理服务器层。WebSphere Application Server 通过使用外部高速缓存实现了这一缓存“推移”。
外部高速缓存
在图 5 中标注的最后的条目是外部高速缓存组。WebSphere Application Server 中的动态高速缓存服务需要持续跟踪外部高速缓存,来保持通信。在 ESI 的处理器部分,必须安装 DynaCacheESI 应用程序。如果已经安装了,点击外部高速缓存组将显示 ESIInvalidator servlet ,这就是安装后所得到的结果。从本质上讲,任何的外部高速缓存(ESI 插件高速缓存、WebSphere Application Server V5 Network Deployment Caching Proxy 或 IBM HTTP Server 中的 AFPA/FRCA 高速缓存)都需要在这里定义外部高速缓存适配器和组。为便于使其无效,可以将相互联系的外部高速缓存条目归为一组;这样就可以一次性的令其失效,而不是一条条地进行。
外部高速缓存
先前,我们为静态文件高速缓存在 Web 服务器上配置 WebSphere Application Server 插件。这些步骤(修改
plugin-cfg.xml 文件中的一个参数和在应用程序服务器上安装 DynaCacheESI )同样适用于动态高速缓存的配置。安装 DynaCacheESI 后,它将通过 ESI 插件为应用程序服务器定义驻留在 Web 服务器上的外部高速缓存。如图 5 所示,如果某一高速缓存条目的 EdgeCacheable 属性设置为真,当被请求时它将向外层推移,通过插件中的缓存进行处理,这样后面的请求中就会进一步地减少应用程序服务器的负载。应用程序将通知插件高速缓存这一条目在何时失效。如前所述,高速缓存条目可以向边缘推移,但应该避免使用服务器端的诸如用户的 HTTP session 值来作为高速缓存的部分标识符。
在网络边缘应用高速缓存
一个安装了 WebSphere Application Server V5 Network Deployment Edge components 的代理服务器,能够在 Web 服务器之前高速缓存内容,这样就可以移除更多网络跳转。
最简单的代理服务器只能高速缓存静态文件。代理服务器的用途就是处理来自后端的服务器(如 Web 服务器)上的内容,而很少处理自身的内容。代理服务器能够高速缓存后端服务器的静态内容。安装了 WebSphere Application Server V5 Network Deployment 的代理服务器可以高速缓存静态的和动态的内容。在图 8 中,我们使用了最新版本的网络图表来展示代理服务器层高速缓存所起到的效益。从中不难看出,当静态的和动态的请求被网络边缘的高速缓存响应时,对响应时间、执行效率和资源的利用都起到了深远的影响。图 8 显示了一个配置了 DMZ 的代理服务器。为了更大幅度地提高效率,一些公司配置和使用了前端代理,把代理服务器放置在远程办公室,用以减轻广域网/局域网的堵塞。
图 8. 代理服务器通过高速缓存相应请求
WebSphere Application Server、插件高速缓存和代理服务器高速缓存之间有很多的差别。代理服务器动态高速缓存只能够缓存整个页面,不能缓存片段,也只能够配置为内存高速缓存或是磁盘高速缓存,不能配置为可向磁盘扩展的内存高速缓存,这些功能 WebSphere Application Server 和插件高速缓存都能够实现。如果使用硬盘高速缓存,则必需配置在一个特定格式的磁盘分区上。它比内存高速缓存速度慢,但是有更大的缓存空间和稳固性,因此在代理服务器崩溃或重启时,高速缓存的数据能够得以保存。
大多数情况下,由于便于实现和避免为代理服务器建立额外的层,用户更倾向于使用插件来实现静态和动态高速缓存。所以,如果在使用上没有特别的需要,用户可能不会单独使用 WebSphere Application Server 来实现高速缓存。当想把代理服务器的高速缓存配置到网络的前端,使其更加接近用户,从而节省网络带宽时,就是使用代理服务器来实现高速缓存的好时机。
高速缓存的高级主题
如果要全面的涵盖高速缓存和所有的高级主题,将需要整整一本著作来叙述,在这里我们要做的是将您的注意力引向两个重要的方面。
首先,我们这里所有的例子都集中于通过配置选项来使用高速缓存,开发人员可以通过为动态高速缓存挂钩编写代码也可以进行高速缓存,确定高速缓存标识,决定是否“立即”让高速缓存失效。比如可以让某些条目只在一天中的某些特定时间进行高速缓存,或者是当达到一定的重复命中次数后进行高速缓存,而当资源不足时使高速缓存失效。关于这些在 WebSphere Application Server V5 JavaDoc 中有全面详细的讲解,请参阅 JavaDoc 中的
com.ibm.websphere.cache 和
com.ibm.websphere.servlet.cache 包。
其次,当我们试图高速缓存内容时,有一点很重要,那就是在 HTML 页面里经常存在头部标记,而这些标记阻碍了我们进行高速缓存。结果由于 HTML 页面反馈了不应该被高速缓存的内容,而致使高速缓存没能实现,人们却误以为已经进行了内容高速缓存。诸如 WebSphere Application Server V5 Network Deployment Caching Proxy 和 IBM HTTP Server 中的高速缓存引擎,他们具有一些强制性的高速缓存特性可以用来忽略这些类型的 HTML 提示。为确保不引起令人不快的行为,在确定每一条高速缓存规则时,您必须和您的开发团队进行协商。至于设置失效,用户必须通知应用程序高速缓存的数据何时变得无效。这可能是高速缓存中最难解决的问题。关于如何配置这些选项的详细信息,请参考这两个产品的信息中心。
数据复制服务
在图 5 中显示的另一特性是利用高速缓存进行复制的能力。准许群组中的多个应用程序服务器之间共享高速缓存数据条目和互相传达这些条目的失效信息。这些特性依赖于 WebSphere Application Server Data Replication Service (DRS)。DRS 也用来在多个应用服务器之间复制用户会话信息。可以将复制的高速缓存设置为共享,这样其他的高速缓存就可以从这个高速缓存中心获取数据,或者是将这个高速缓存复制到所有其他的服务器上。图 6 描述了“
共享原则 ”标记,它可以指定是否将高速缓存条目排除在共享之外,或者是如何共享。有很多的选项来指定是否将高速缓存条目自动复制,或者是通过命令复制到其他高速缓存。这是一个高级的配置管理,首先需要理解如何配置 DRS 和创建复制域,对此在 WebSphere Application Server Information Center 有更加深入的探讨。
安全提示:DRS 通信的默认配置是不可靠的。它是为获取最高的性能而配置的,如果要想保证在服务器之间复制的高速缓存数据不会泄漏和篡改,就应该对 DES 进行加密。
高速缓存的故障诊断
对于高速缓存的故障诊断,有几个非常有用的工具。在应用服务器端,利用 WebSphere Application Server 强有力的跟踪功能,可以获得详细的跟踪信息。这些可以通过在跟踪配置页面,选择任意不同的高速缓存的组合或是通过跟踪生成字符串
com.ibm.ws.cache=all=enabled 来实现。通过分析跟踪文件,我们可以浏览保存和处理高速缓存条目的决策过程,以及一些高速缓存没有起到预期的效果的原因。值得注意的是,在插件中也具备跟踪功能,所以插件也能很好地实现跟踪功能。在这一章前面所提到的高速缓存监视器也同样非常有用。通过高速缓存监视器提供的连接,就可以获得详尽的关于高速缓存标识符和其他方面的有用信息。如果您没有发现一个被缓存的 servlet,但是请相信所有的这个 servlet 的别名都已经配置为高速缓存。总的来说,所有我们能从浏览器的地址栏中见到的服务,都可以通过 servlet/JSP 缓存来提供。
在静态文件高速缓存的故障诊断中,日志文件是非常有用的。IBM HTTP Server 和 WebSphere Application Server V5 Network Deployment Caching Proxy 将高速缓存命中率从日志文件中分离出来,同时通过标准的日志来记录高速缓存“故障”。切记,如果您没有在日志文件中看见这些条目,很可能是因为浏览器端的网络临时文件正确的响应了您的请求。由于这个原因,通过不同的工作站测试高速缓存并且在每一次请求后都删除网络临时文件,通常是一个很有效的方法。WebSphere Application Server V5 Network Deployment Caching Proxy 的管理控制台有一个代理访问页,在这个页面里,用蓝颜色记录了从高速缓存返回来的条目,但是只有最近的几十条记录;日志文件只能通过一次列出所有条目的方式查看。
切记,任何的高速缓存都需要多次的“点击”后才能够被加载——所以不要在第一次请求后就期望可以看到高速缓存条目。
结束语
在这篇文章里,我们涵盖了 WebSphere Application Server 所提供的高速缓存的大部分功能。从图 9 中我可以看到 WebSphere Application Server 高速缓存的体系结构,您会发现这也许会很有用,同时也相当有趣。
图 9. WebSphere Application Server 动态高速缓存体系结构
高速缓存是一个高级的主题,为使性能得到极大的提升,非常值得对它进行更加深入的研究,您可以在 WebSphere Application Server Information Center 获得更多的信息。
参考资料
关于作者  | |  |
Bill Hines是一位认证顾问 IT 专家,作为流动顾问在 IBM Software Services for WebSphere 部工作。他为 IBM 客户提供技术指导、技能转移、开发、故障诊断和服务优化等方面的咨询服务。以前,Bill 有一家私人咨询公司,与其他一些大公司合作提供关键的体系结构和开发规划方面的技术支持。
|
对本文的评价
|