很多 SOA 实现都依赖于用 XML 定义的消息格式。结果,消息模式可能变得非常复杂、不兼容、难以维护,甚至造成严重的可伸缩性和性能问题。在本文中,Frank Cohen 将介绍如何通过在 SOA 中间层使用 XML、XQuery 和本机 XML 数据库技术来提高 SOA 性能的战略和技术。
很多软件架构师在面向服务体系结构(SOA)设计中使用 XML,虽然没有一种 SOA 标准要求在 SOA 中使用 XML 或者提供相关指南。因此,软件开发社区做了很多实验和调查来发现定义服务端点和消息定义(模式)的最佳方式。这些方法大多数都会带来了糟糕的性能和可伸缩性。
比如,最早提出用 SOA 实现 ebXML 的 General Motors Corp.,其最初的设计使用的是 Universal Business Language (UBL),建立的 XML 消息有 150,000 字节到 10 兆字节甚至更大。2004 年,我的性能测试公司 PushToTest 认为当时的 Java™ 应用程序服务器没有提供足够的吞吐量,在 GM Web Services Performance Benchmark 研究中提出了可伸缩性和性能问题。
当时基于 XML 的 Web 服务技术还非常新,我认为新一代应用程序服务器技术会解决性能问题。但大部分问题仍然存在。
Web 服务吞吐量问题和复杂的 XML
2005 年,PushToTest 完成了一项新的 SOA 性能研究(请参阅参考资料)表明,在处理复杂的 XML 消息时,使用当前 Java 应用程序服务器构建的应用程序其性能很差,不足以投入生产。是发现的问题和以前研究中的问题相同:
- 简单对象访问协议(SOAP)绑定(代理)低效而缓慢。
- 每次请求都需要一组全新的资源(对象、CPU 和网络带宽)来处理响应。没有缓冲模式。
- 使用关系数据库技术存储 XML 数据非常慢而且没有可伸缩性。
为了了解这三个问题,设想一下软件开发人员如何使用 J2EE 应用程序服务器工具构建和部署 XML 服务。
图 1. WSDL 定义的例子虽然可以使用使用不同技术建立基于 XML 的 Web 服务,但我发现多数开发人员更愿意从服务的 Web Services Description Language (WSDL) 定义开始。Java 应用程序服务器提供了输入 WSDL定义和生成代理类的工具。代理器接收 SOAP 请求并把请求转发给 Java 对象或 Enterprise Java Bean (EJB) 进行处理。SOAP 绑定(代理器)是一种 Java 类,可通过 servlet 接口调用它。
图 2. Java 方法调用图 2 说明了 Web 服务消费者如何向服务发出 SOAP 请求。SOAP 绑定对 SOAP 消息体中的 XML 内容进行反序列化。这个过程需要进行大量的处理,非常复杂,因为消息体常常包含复杂的数据类型。比如,消费者可能向服务发送包含多个值的散列表。SOAP 绑定需要解码散列表的内容,对每个值创建 Java 对象的实例。散列表还可能包含其他散列表,因此解码 SOAP 消息内容不是一件容易的事。如果不相信的话,请看一看 Apache Axis 反序列化器的源代码。
SOAP 绑定实例化包含 SOAP 消息体内容的 Java Request
对象。SOAP 绑定调用目标类中的目标方法,并将 Request
对象作为参数传递。目标 EJB 或 Java 对象提供所有必要的处理来建立对请求的响应。SOAP 绑定将 EJB 或 Java 对象返回的值序列化到 SOAP 响应消息中。SOAP 绑定将响应对象中的值解码成能够序列化到 SOAP 响应消息中的值需要经过同样复杂的过程。
通过研究用流行的 Java 应用程序服务器工具创建的 SOAP 绑定,我发现了以下问题:
- 应用程序服务器工具创建的 SOAP 绑定效率很低。比如,我发现某些 SOAP 绑定创建 SOAP 请求的多个副本,每个请求都实例化为
String
对象 —— 这样做并没有明显的理由。一些 SOAP 绑定创建了 15,000 个 Java 对象来反序列化消息体中包含 500 个元素的 SOAP 请求。
- 在配备有双 CPU 3.0 GHz Intel Xeon 处理器的服务器上,我发现在处理有效负荷 10,000 字节、包含 50 个元素的简单 SOAP 消息时,每秒处理的事务为 15 到 20 个(TPS)。随着 SOAP 消息复杂性和大小的增加,我发现会造成明显的伸缩性和性能问题。有效负荷 100,000 字节、包含 750 个元素的 SOAP 消息会把吞吐量降低到 1.5 TPS。SOAP 消息体中元素个数越多,每个元素嵌套越深,问题就会越严重。
性能问题在 SOA 设计中会引起连锁反应。SOA 是一种组件软件重用技术。一个服务通常调用其他服务来确定对消费者请求的响应,从而形成一个调用链。
图 3. 消费者不仅仅是一个服务的性能问题,每个服务在序列化、反序列化请求和响应的时候都会增加同样的开销。随着服务调用层次的增加,性能问题也成倍增加。
加快 SOA 失去的机会
除了 SOAP 绑定代理速度慢的问题,SOA 设计还常常忽略或者忽视另外两个问题。
首先,SOA 设计常常忽视了用中间层服务缓冲来提高 SOA 性能的可能性。比如多数 SOA 设计中的 XML 模式都定义了了响应的 time-to-live 值。在这种情况下,缓冲服务响应并在服务再次收到同样的请求时重新使用缓冲的响应是一种提高 SOA 服务性能的合理而适当的方法。
其次,在 SOA 性能测试中,我尝试了各种不同的 XML 消息解析方法,其中包括 Streaming API for XML (StAX)、XML 绑定编译器、Java Architecture for XML Binding (JAXB) 和 Document Object Model (DOM) 技术。一些技术的性能要优于另一些。比如,很多 StAX 解析器能够提供比 DOM 解析器快 2 到 10 倍的性能。
我怀疑如果使用其他东西而不是 Java 对象来提供 SOAP 绑定是否能够改进性能。比如,如果收到的 SOAP 请求在本机 XML 环境中处理,那么基于 Java 的 SOAP 绑定就不再是必需的了,同时还可以避免因为序列化成 Java 对象而导致的性能降低。
此外,一些本机 XML 环境使用 Java Virtual Machine 环境,但会避免构造 Java 对象。比如,Raining Data 的 TigerLogic XDMS 和 Kawa/Qexo 通过将 XQuery 查询直接转换成 Java 字节码来实现 XML 处理代码。这样做是因为与使用 Java 对象相比,使用 XQuery 字节码实现的 XML 处理代码的吞吐量更大,代码更短。
FastSOA 解决方案
FastSOA 是解决这些问题的一种体系结构和软件编码实践:
- FastSOA 通过减少 Java 对象的需要,更多使用本机 XML 环境提供 SOAP 绑定来解决 SOAP 绑定(代理)性能问题。
- FastSOA 引入了中间层服务缓冲来加快 SOA 服务。
- FastSOA 使用本机 XML 持久性来避免 XML 到关系数据库的转换造成的性能问题。
下图显示了 FastSOA 体系结构。
图 4. FastSOA 体系结构FastSOA 体系结构与现有的基于 Web 的基础结构结合在一起,作为中间层缓冲部署来接收服务消费者的请求。比如,一个消费者向服务发出 SOAP 请求。中间层缓冲提供 SOAP 绑定(代理)。绑定调用 XQuery 在 XQuery 引擎处理 XML 请求文档。XQuery 检查缓冲,查看以前是否收到该请求;在这种情况下,FastSOA 服务可以从缓冲中返回响应,不需要逆流而上再请求服务。该过程通过缓冲加快 SOA 执行从而实现了 SOA 提速。
FastSOA 方法的优点包括:
- 服务端点是标准的。对于应用程序的其他部分而言,FastSOA 中间层缓冲就像是一种服务。
- 不需要修改现有的系统或代码。FastSOA 中间层缓冲作为一种数据聚合和迁移服务嵌入到已有的数据中心。
- 如果上游服务暂时不能用,当服务离线的时候,FastSOA 方法提供了一种浏览缓冲数据的机制。
- 通过缓冲服务的请求降低了为支持消费者和服务之间的通信通常所需的带宽要求。
为了从实践的角度理解 FastSOA,考虑下面的应用程序。
XML 的例子
General Motors 采用 SOA 模式创建服务,让汽车代理商使用基于 ebXML 的模式和协议从生产厂家订购零部件。该服务能够识别 Software Technology in Automotive Retailing (STAR) 组织的一种 XML 模式。STAR 是大型汽车厂商共同努力的结果,其中包括 GM。STAR 创建并维护 Business Object Document (BOD) 模式,定义了目录检查请求(以及其他许多东西)。
CheckInventory
请求检查请求者和目录的级别与状态。服务消费者根据 STAR 模式创建目录请求文档。消费者将文档编组成请求,并通过网络发送给服务。服务发回目录状态响应说明库存中有哪些零部件。
通过降低网络带宽的需要和减少为了响应冗余请求而造成的服务带宽需要,零件订购服务可以从 FastSOA 模式中受益。
比方说,汽车零售商的零件目录响应中包含一个 Time-To-Live (TTL) 元素。TTL 元素定义了响应有效的秒数。比如 GM 可能将其设为 60 秒。在这 60 秒内,FastSOA 用中间层存储的目录响应缓存响应目录请求。这样服务就减少了带宽的使用,并缩短了请求响应时间。
下表说明了如何计算网络中的服务提速效果,这些服务位于本地网络之外的服务器上,FastSOA 数据缓冲收集服务在本地网络中。
表 1. 计算服务加速效果动作 | 无缓冲² | 启用缓冲² |
---|
第一次请求处理的时间 | 1765¹ | 2218¹ |
在缓冲中存储请求的时间 | 0¹ | 453¹ |
后续相同或冗余请求 | 1765¹ | 320¹ |
使用的 Internet 带宽 | 30,400 K 字节 | 304 K 位 |
使用的总时间 | 2941 分钟 | 533 分钟 |
¹这里所有的时间都是毫秒,1 秒 = 1,000 毫秒。 ²假设:
- 消费者和缓冲服务使用 100 M 以太网连接和 1.5 M 左右的 DSL 连接。
- Time to Live (TTL) 为 60 秒。
- 请求/响应包含 38,000 个字节。
- TTL 期间有 100,000 次请求。
| | |
在 FastSOA 实现中,用 XQuery 实现零部件订购服务。XQuery 请求目录服务,读取响应的内容,在运行时确定是否可以使用以前存储的响应而不必再次请求目录服务。
这样就在服务环境中实现了 FastSOA 数据缓冲收集体系结构。XQuery 和本机 XML 数据库提供了重用以前缓冲响应数据的服务,只要请求与以前的请求匹配并且数据仍然不过时。结果是服务提速了。
FastSOA 技术选择
可以使用 Java 代码和关系数据库技术实现 FastSOA 体系结构。但是,在测试使用 Java 对象创建的服务绑定和使用关系数据库持久 XML 时,我发现了重要的性能和可伸缩性问题。这些问题很突出,考虑使用 XQuery、XSLT 和本机 XML 数据库技术很有必要。
我对 XQuery 感兴趣,是因为它是作为应用程序开发的本机 XML 环境来实现的。与早期的 Java 技术非常相似,XQuery 社区充满了扩张和证明 XQuery 是一种开发平台的活力。实际上,多数 XQuery 实现都经过扩展超出了 XQuery 标准,以便 XQuery 能够进行 SOAP 请求。比如,XQuery 可以查询其他服务、J2EE 对象和通过 JDBC、SOAP、JMS 协议查询数据源。此外,已经有 10 种或更多非常可靠的商业化和开放源码 XQuery 实现。
最后,FastSOA 使用本机 XML 数据库作为中间层缓冲,因为 SOA 数据通常采用 XML 编码格式,而关系数据库在持久存储和索引 XML 这样的层次性非结构化数据方面有很大不足。存储 XML 数据的关系数据库通常使用大型二进制对象(BLOB)字段类型存储 XML。不仅效率低,而且很难建立索引以便快速搜索。对于流数据采用关系方法通常也不是最佳办法。如果在基于 Web 服务的网络中发送 XML 消息,最好用基于流的方法处理该消息,而关系数据库对此无能为力。
FastSOA 的未来
除了本文所述的 SOAP 绑定性能改进之外,采用中间层服务缓冲还会为企业带来很多好处。其他好处包括中间层模式转换、服务版本化、策略选路和服务质量(QOS)处理。比如,FastSOA 提供了中间层 XML 消息模式转换,以便保证需要不同和不兼容的消息类型的服务之间的兼容性。
结束语
本文考察了如何提升 SOA 的性能和可伸缩性,详细介绍了在中间层使用 XQuery 支持结合 XML 持久的 SOA 设计所带来的好处。FastSOA 设计结合使用了本机 XML 持久性和 XQuery,因此每次收到服务调用时,中间层都要决定是使用以前请求的缓冲值响应,还是传递请求。服务使用 XQuery 根据对服务请求元数据查询的结果描述判定缓冲是否有效。