级别: 中级 Arnauld Desprets, IT 架构师, IBM, Software Group
2009 年 8 月 26 日
使用定制的中介获取定制元数据(比如 IBM® WebSphere® Registry and Repository 中的策略)时,请求的数量变得非常大,可能会大大增加对资源的需求。降低负载、缩短响应时间的一个方法是使用缓存。本文为您介绍如何扩展 WebSphere Enterprise Service Bus (ESB) 中部署的中介元数据,以增加缓存支持。
来自 IBM WebSphere Developer Technical Journal。
简介
通过定制中介集成 IBM WebSphere Enterprise Service Bus (ESB) 和 IBM WebSphere Service Registry and Repository(后文简称 Service Registry)时,性能问题很快就能得到解决。实际上,在大部分情况下,在中介中添加缓存功能不仅能缩短响应时间,而且能减少 Service Registry 服务器本身占用的资源。
本文是 Andre Tost 的文章 使用 WebSphere Service Registry and Repository 和 WebSphere ESB 建立策略驱动的 SOA 的续文,该文解释了中介如何使用在 Service Registry 中定义并在 WebSphere ESB 中执行的定制 WS-Policy 策略。本文中的信息不仅关注缓存的实现细节,而且关注其他重要的考虑事项,比如如何监控和调整缓存。
André 的文章使用示例 “审计” 策略,其场景如下:
在 TemperatureConverter 服务之前的中介检查是否向服务附加了 Audit 策略(在 Service Registry 中定义)。如果存在,那么在请求消息处理过程中会调用 Audit 元数据。图 1 展示了 CelciusToFahrenheit 操作的流程图,您可以从中看到定制元数据。
图 1. 流程图
后文将介绍如何更改定制的 Java™ PolicyRetrieval 元数据并添加缓存。
架构考虑事项
在介绍实现之前,我们首先了解一下使用缓存时面临的一些情况:
-
简单场景
简单的情况下,您只有一个 WebSphere ESB 服务器实例,因此只部署一个中介。不需要担心数据重复。您接受这样一个事实,缓存中的数据一段时间后可能不再有效。您只管从缓存存取数据。数据有效性通过缓存本身的时间超时决定。在后文您将看到,在这种场景中添加缓存非常容易。
-
更复杂的场景
在这种情况下,缓存任何时候包含无效数据都是不可接受的,至少您应该尽最大的可能减少数据无效的时间。要做到这一点,可以考虑在服务操作附件的策略发生特定更改时通知缓存。换句话说,更改策略将造成缓存中的数据无效。在注册表这方面,可以使用 Service Registry “notifier” 插件实现。缓存方面的实现非常简单,因为缓存已经公开了一个方法使条目无效。唯一的难点在于从 Service Registry 访问缓存,因为它可以在不同的单元运行。有几种方法实现这一点,最简单的方法是创建一个可以在缓存所在服务器部署的 Web 服务或 Web 应用程序。然后,Service Registry 将通过该接口调用缓存实例。
缓存考虑事项
使用缓存时要记住几个关键事项,有些已经在另一篇文章中阐述。我们从 developerWorks 文章 请求端缓存模式规范,第一部分:请求方端缓存模式概述 中的几个重要考虑事项开始。以下内容摘自这篇文章:
-
数据键
缓存的基本要求是,缓存的数据可以明确、唯一地确定。可缓存数据项的标识符称为键。基本上,缓存支持使用指定键插入数据项,之后还可以使用相同的键检索该项。因此,如果两个数据项表示不同的信息,那么它们的键必须不同。每个数据项的键必须是唯一的。
考虑到本文的目的,我们采取简单的方式处理,假设服务操作的名称和名称空间可以确定它的唯一性。同时假设您只有一个 Service Registry 逻辑实例包含操作的策略和策略附件定义。
-
数据不稳定性
如果与键关联的数据发生了变化,而之前的数据已经置入缓存,那么使用该键从缓存中检索到的数据将是过时的数据。数据不稳定性问题往往是使用缓存的最大限制。处理这种情况有两种基本战略:缓存项目超时和缓存项目明确无效:
-
工作集大小
缓存有一个工作集大小。这是需要在缓存中保持的项数量,以保证项的大部分请求都能通过缓存得到满足。通常,缓存可以包含的项数量有一个限制,称为容量。如果添加的项超过该值,那么将会挤出一些项。缓存实现的挤出策略是最近最少使用的(LRU)先出。该策略挤出最少引用的项。使用该策略,如果缓存容量足够大,那么它的工作集将在缓存中得以维护,在加速对目标组件的请求时将很有效率。但是,如果缓存容量太大,那么将造成空间浪费。如果太小则无法满足工作集需求,缓存的效率将相对较低。
因此,监控缓存行为的重要性在于最大化其效率,这也是本文介绍如何监视缓存的目的。
-
预载和缓存刷新
在负载很大时,需要考虑其他事项以防止使用缓存和 Service Registry 时发生其他事件。有一种很少发生的情况:某个项已经不在缓存中或者由于某种原因无效(例如,缓存已满或者缓存大小太小)。如果向 Service Registry 服务器发送大量相同的请求检索相同的值,那么将出现以下两种结果:
- Service Registry 服务器接收到大量相同的请求。
- 由于高负载导致收到结果的响应时间非常长。
您可以使用 mutex 技术消除这种影响,这种技术使您能独占访问共享资源。在这种情况下,您应该使用 mutex 阻塞对一个缓冲项目的信息检索(如果可能)并阻塞对同一个 ESB 资源的其他请求。可以在 Web 上找到 mutex 的 Java 实现(见 参考资料)。
另一个要考虑的情况与预载机制的使用有关,这与前一种情况类似。在这种情况下,您需要在应用程序启动或者可能导致高负载的其他预定义事件发生时将数据加载到缓存。这将减少第一次请求的响应时间。同样,实现这一点有几种方法,比如使用通过 ESB 或 ESB 实例时可以向服务发送测试数据的定制应用程序。
记住所有这样情况,现在让我们看看熟悉的 WebSphere Application Server 实现。
使用 DistributedMap
要实现缓存,您需要使用 DistributedMap 接口、J2EE® 应用程序和系统组件搜索对缓存对象的引用,以缓冲和共享 Java 对象。图 2 提供了一些关于分布式映射的详细信息。您可以识别各种高级缓存的功能,比如在几个缓存实例之间执行复制的功能、卸载磁盘内容的功能、替换策略实现等等。
图 2. DistributedMap 概述
在本例中,您可以使用 WebSphere Application Server 管理控制台配置缓存实例。(替代方法见 WebSphere Application Server Information Center)。
代码更改
正如您将看到的,实现缓存只需要几行代码。这里最重要的类是 PolicyQueryTest。您可以在 hasPolicyDefined(String, String, String) 方法中找到对缓存的调用。以下清单 1 展示了该方法的摘要。
清单 1
1 List<String> policies = null;
2 long beforeCache = System.currentTimeMillis();
3 CacheKey cKey = new CacheKey(policyElementName, namespace, operation);
4 logger.logp(Level.FINEST, CLASS_NAME, METHOD_NAME, "Checking if there is not already
an entry in the cache with key {" + cKey + "}");
5 policies = (List) CacheManager.getInstance().get(cKey.toString());
6 if (policies == null) {
7 try {
8 policies = runQuery(namespace, operation);
9 long afterMiss = System.currentTimeMillis();
10 logger.logp(Level.FINEST, CLASS_NAME, METHOD_NAME, "CACHE Miss: key {" + cKey + "}
retrieved in " + (afterMiss - beforeCache) + " ms");
11 // If policies are null should we add it
12 if (policies == null) {
13 logger.logp(Level.FINEST, CLASS_NAME, METHOD_NAME, "No policies applies to this
operation");
14 } else {
15 logger.logp(Level.FINEST, CLASS_NAME, METHOD_NAME, "Adding the polices in the
cache");
16 CacheManager.getInstance().put(cKey.toString(), policies);
17 }
18 } catch (Exception x) {
19 logger.logp(Level.SEVERE, CLASS_NAME, METHOD_NAME,
"Exception during policy retrieval
: " + x.toString());
20 throw new RuntimeException(x);
21 }
22 } else {
23 long afterHit = System.currentTimeMillis();
24 logger.logp(Level.FINEST, CLASS_NAME, METHOD_NAME, "CACHE HIT: key {" + cKey + "}
retrieved from cache in " + (afterHit - beforeCache) + " ms");
25 } |
- 在清单 1 的第 3 行,您创建与策略名称、操作名称和名称空间相关联的键。操作名称和名称空间是中介元数据接口的一部分。CacheKey 类是一个简单的串联。如果同一个操作有不同的版本,您可以获取相同的键,除非操作名称或名称空间表达有关操作或服务的版本信息不同。通常,执行服务管理时这是一个很好的实践。您还应该让 CacheKey 是人类可读的,以防需要使用缓存监视应用程序移除项。如果缓存中的项只能通过编程方式移除(除了移除缓存本身外),那么您可以使用哈希值等删除项。
- 在第 5 行,您尝试使用刚刚生成的键从缓存中获取值。如果没有项,您应该使用名称空间和操作查询注册表,并将结果保存到缓存中(第 16 行)。
清单 2 展示了 CacheKey 的主代码。正如您看到的,这只是简单地串联了名称和名称空间,但是要确保每种情况的唯一性要复杂得多。这里有趣的行是第 11 行,其中包含了键值。
清单 2
1 public class CacheKey {
2 private String policyElementName;
3 private String namespace;
4 private String operation;
5 public CacheKey(String policyElementName, String namespace, String operation) {
6 this.policyElementName = policyElementName;
7 this.namespace = namespace;
8 this.operation = operation;
9 }
10 public String toString() {
11 return policyElementName + "." + namespace + "." + "." + operation;
12 }
13 } |
本文代码的组织
为了帮助您执行环境测试,本文提供了一个 样例代码。下文是一些解释该代码是如何组织的信息。表 1 列出了可以用来运行完整测试的模块。与 之前的文章 不同,该示例使用服务提供商的本地实现。
表 1
| 项目 | 描述 |
|---|
|
TemperatureConverterProviderEAR
| 包含 TemperatureConverter 服务实现的 EAR | |
TemperatureConverter
| TemperatureConverter 服务实现 | |
TemperatureConverterClientEAR
| 包含 TemperatureConverter 服务使用者的 EAR | |
TemperatureConverterClient
| TemperatureConverter 服务使用者实现,该代码通过中介模块公开的 WSDL 生成 | |
PolicyMediation
| 中介模块 | |
PolicyQuery
| 包含中介模块的 Java 实现和代码实现的库 |
配置 WebSphere ESB 中的缓存
首先,启动 WebSphere Application Server 管理控制台配置缓存。导航到 Resources =>
Cache instances => Object cache instances。单击 New 按钮,下文将列出字段值,然后单击 OK(图 3):
- Name:
WSRRPolicyCache
- JNDI Name:
services/cache/PoliciesQueries(这是前文代码中的引用)
图 3. 缓存实例配置
测试应用程序
现在,我们假设您已经安装了两个 TemperatureConverter 使用者和提供者企业归档文件,并且在 WebSphere ESB 中发布 Policy 中介。
Service Registry 中的测试数据由以下内容组成:
- TemperatureConverter 服务的服务提供商的 WSDL:TemperatureConversions.wsdl(图 4)。
图 4. SWRR 中的 WSDL 文档
- 包含审计策略的策略文件:audit.xml。
- 策略附件服务:auditatachment.xml(图 5)。
图 5. 策略文件
现在,您可以开始运行测试:
- 为了查看缓存内容,请确保已将缓存内容发送到 WebSphere 管理控制台的
*=info: com.ibm.sample=all。
- 在浏览器中,输入 Web 服务客户端测试应用程序的 URL,本例中为:
https://localhost:9443/TemperatureConverterClient/jsps/TestClient.jsp。
- 单击 celciusToFahrenheit(java.math.BigDecimal) 链接并输入任何值。
- 单击 Invoke 按钮。第一次单击应该从 Service Registry 查询策略并将其放入缓存。您应该获得类似于图 6 所示的结果。
图 6. Web 服务调用结果
- 现在,输入任何新值并再次单击 Invoke。第二次单击将在缓存中找到策略。
- 在内容输出中,查看清单 3 中展示的行。
清单 3
TT T1 SystemOut O Operation name is : CelciusToFahrenheit
TT T1 SystemOut O Operation namespace is : http://webservices.daehosting.co
m/temperature
TT T1 sample > com.ibm.sample.PolicyQueryTest hasPolicyDefined ENTRY
TT T1 sample 3 com.ibm.sample.PolicyQueryTest hasPolicyDefined policyEleme
ntName: tns:Audit
TT T1 sample 3 com.ibm.sample.PolicyQueryTest hasPolicyDefined Checking if
there is not already an entry in the cache with key {tns:Audit.http://webserv
ices.daehosting.com/temperature..CelciusToFahrenheit}
TT T1 sample 3 com.ibm.sample.PolicyQueryTest runQuery Policy document: [B
@43fc43fc
TT T1 sample 3 com.ibm.sample.PolicyQueryTest hasPolicyDefined CACHE Miss:
key {tns:Audit.http://webservices.daehosting.com/temperature..CelciusToFahren
heit} retrieved in 344 ms
TT T1 sample 3 com.ibm.sample.PolicyQueryTest hasPolicyDefined Adding the
polices in the cache
TT T1 sample 3 com.ibm.sample.PolicyQueryTest hasPolicyDefined Policy name
: tns:Audit
TT T2 SystemOut O Operation name is : CelciusToFahrenheit
TT T2 SystemOut O Operation namespace is : http://webservices.daehosting.co
m/temperature
TT T2 sample > com.ibm.sample.PolicyQueryTest hasPolicyDefined ENTRY
TT T2 sample 3 com.ibm.sample.PolicyQueryTest hasPolicyDefined policyEleme
ntName: tns:Audit
TT T2 sample 3 com.ibm.sample.PolicyQueryTest hasPolicyDefined Checking if
there is not already an entry in the cache with key {tns:Audit.http://webserv
ices.daehosting.com/temperature..CelciusToFahrenheit}
TT T2 sample 3 com.ibm.sample.PolicyQueryTest hasPolicyDefined CACHE HIT:
key {tns:Audit.http://webservices.daehosting.com/temperature..CelciusToFahren
heit} retrieved from cache in 0 ms |
您可以从这些内容中看到,第一个时间处缓存中没有项,请求用了 344 毫秒(请参见清单 3 中粗体显示的消息 CACHE Miss),在策略存在于缓存中的第二个时间处,不需要查询 Service Registry,响应时间为 0 毫秒(请参见同样用黑体显示的 CACHE HIT)。(这些数字不表示实际的生产环境)。
一个有用的提示:您可以使用 REST 接口验证策略是否应用正确:https://localhost:9444/WSRR/6.1/Metadata/XML/GraphQuery?query=/WSRR/WSDLPortType[@name=%27TemperatureConversionsSoapType%27%20and%20@namespace=%27http://webservices.daehosting.com/temperature%27]/operations[@name=%27CelciusToFahrenheit%27]。
探讨要使用通知的缓存场景
您可能已经发现向中介元数据添加缓存非常容易。接下来,您将学习更加复杂的场景,不仅要像以前一样使用缓存,还应该能够明确通知它需要做出的策略更改。在这种情况下,正如上文所示,您将使用 Service Registry 的通知功能用编程方式验证与策略相关的缓存项。
首先快速浏览 Service Registry 中建模的策略。这将帮助您理解如何查找受策略更改影响的操作。最终,您需要计算缓存中需要更新的键。
图 7. Service Registry 中的策略模型
除了这里仍然应用的明确策略更改,还有其他场景;换句话说,还有其他影响中介结果的实体。一个示例是,附件由于向另一个操作新实体添加了策略而发生了更改。如何更新策略或其他实体的详细信息超出了本文的范围,因此我们不再深入讨论。这里要做的是捕获更改策略中的事件。
这里感兴趣的是逻辑对象。查看所有附加到该策略的操作,您将找到需要更新的缓存中的对象。记住,缓存键是 policyElementName、操作名称和操作名称空间的串联。使用该键,您就可以更新缓存策略信息中的所有中介。
本文不提供该场景的完整实现,但是为您提供了一些重要的实现元素。考虑以下情况:不同中介的配置可以频繁更改(例如,添加 WebSphere ESB 的新实例),需要轻松访问和更新。要支持这一点,您可以使用 Service Registry 标准 “用户配置” 指定服务器属性。用户配置可以通过配置视图在 Service Registry 中直接管理。清单 4 展示了用户配置文件的样例。
清单 4
<ServerConfigurations>
<ServerConfiguration hostname="127.0.0.1" port="2819" />
<ServerConfiguration hostname="127.0.0.1" port="2820" />
</ServerConfigurations>
|
要加载用户配置文件,可以使用 Service Registry 管理控制台。转到配置视图,然后导航到 Active Configuration Profile => Plug-ins => User
Configurations。选择 Load User Configuration 并输入相应的文件名。
清单 5 展示了使用该配置文件的代码片段。该代码位于 CacheNotifier Java 项目的 com.ibm.sample.wsrr.notifier.userconfig 包中。
清单 5
AdminService adminService = AdminServiceFactory.getAdminService();
byte[] bytes = null;
// Locate the ServiceRegistryRepository MBean
ObjectName queryName;
try {
queryName = new ObjectName("WebSphere:type=ServiceRegistryRepository,*");
Set s = adminService.queryNames(queryName, null);
// Find the first MBean reference
Iterator iter = s.iterator();
ObjectName mBean = null;
if (iter.hasNext()) {
mBean = (ObjectName) iter.next();
// got one
// Format the call parameters
String[] signature = new String[] { String.class.getName(),
String.class.getName() };
Object[] params = new Object[] { configName, configType.toString() };
bytes = (byte[]) adminService.invoke(mBean, "retrieveConfiguration",
params, signature);
}
} catch (MalformedObjectNameException e) {
...
} |
该代码通常从 Service Registry 通知程序调用。CacheNotifier Java 项目中的 com.ibm.sample.wsrr.notifier 包中提供了一个非常简单的样例。要配置该通知程序,请参见 Service Registry Information Center 中的 配置定制插件 小节。
使用扩展的缓存监视器
现在,返回到第一个运行场景,查看缓存的调整。在这里,我们只解释一下如何使用缓存监视器提供有关缓存行为的有用信息。然后可以使用提供的数据微调缓存 —— 更改其配置、大小、超时、是否需要使用磁盘卸载等等。您还可以验证提示和遗漏的数量,然后检查缓存的效率。
要安装缓存监视器应用程序,您首先需要安装 WebSphere Application Server 提供的 默认的缓存监视应用程序,然后使用 Extended Cache Monitor 技术预览 中提供的内容更新该应用程序。完成安装之后,作为安全角色登录以执行用户/组映射:
- 在浏览器中,输入缓存监视 Web 应用程序 URL,在本例中为:
http://127.0.0.1:9080/cachemonitor/logon.jsp。
- 输入有效的用户 ID/密码。
- 在可选菜单中(图 8),选择想监视的缓存实例。在本例中为:services/cache/PoliciesQueries。单击 OK。
图 8. 缓存统计数据 —— 选择正确的实例
- 单击左导航面板的 Cache Statistics 链接。在该测试中,您可以看到缓存包含一个项,它被点击 17 次,有 3 次没有包含搜索的值(图 9)。
图 9. 存储统计数据 —— 结果
- 通过单击 Cache Contents 链接,您可以获取该缓存实例的内容(图 10)。
图 10. 缓存内容
如果需要,您可以从缓存监视应用程序中清除缓存或使特定的项无效。
结束语
本文展示了在 WebSphere Enterprise Service Bus 中使用 DistributedMap 特性如何帮助您快速向中介流组件添加缓存。首先回顾了各种添加缓存的场景,然后用一个可下载的示例展示如何添加缓存。本文的末尾快速浏览了一个缓存监视应用程序。
致谢
作者感谢 IBM Software Services for WebSphere 团队的 André Tost 为本文提供帮助。
下载 | 描述 | 名字 | 大小 | 下载方法 |
|---|
| 样例代码 | PMWC_example_materials.zip | 334 KB | HTTP |
|---|
参考资料 学习
获得产品和技术
关于作者  | 
|  | Arnauld Desprets 是英国 IBM Software Services for WebSphere 的一名 IT 架构师。Arnauld 在欧洲有 10 多年的公司咨询经验。在 WebSphere Application Server 方面有 8 年以上的经验,目前专攻 SOA 和 Web Services,尤其是 WS-Security。他最近对治理角色和注册表很感兴趣,包括 SOA 中的 WebSphere Services Registry and Repository。 |
对本文的评价
|