消息等待时间和消息吞吐量是两个主要的性能指示器,它们通常取决于典型消息在完成消息传送过程中的各个步骤时所需的时间。下列步骤显示了如何以持久、可靠的方式传送消息。这些步骤将在插图之后介绍。
消息从生成方客户端传送到代理。
代理读取消息。
消息被放置到持久性存储库当中(出于可靠性的考虑)。
代理确认收到消息(出于可靠性的考虑)。
代理确定消息的路由。
代理写出消息。
消息从代理传送到使用方客户端。
使用方客户端确认收到消息(出于可靠性的考虑)。
代理处理客户端确认(出于可靠性的考虑)。
代理确定已经处理客户端确认。
因为这些步骤是连续的,所以任何步骤都可能成为消息从生成方客户端到使用方客户端的传送过程的瓶颈。这些步骤中的大多数都取决于消息传送系统的物理特征:网络带宽、计算机处理速度和消息服务体系结构等等。但是,有一些步骤还取决于消息传送应用程序的特征和该应用程序要求的可靠性级别。
以下各节讨论应用程序设计因素和消息传送系统因素这二者对性能的影响。尽管应用程序设计和消息传送系统因素在消息传送过程中紧密交互,但它们彼此是独立的。
应用程序的设计决策对消息传送的总体性能有很大影响。
对性能影响最大的主要是那些影响消息传送的可靠性因素。其中包括下列因素:
其他影响性能的应用程序设计因素有:
接下来的几节讲述其中的每个因素对消息传送性能所产生的影响。通常,应当在性能与可靠性之间进行权衡。提高可靠性的因素可能会导致性能降低。
表 11–1 显示各个应用程序设计因素大体上如何影响消息传送性能。该表显示了两个方案(一个高可靠性、低性能方案和一个高性能、低可靠性方案)和分别对应于这两个方案的应用程序设计因素选项。在这两种极端情况之间,有很多可以同时影响可靠性和性能的选项和折衷。
表 11–1 高可靠性和高性能方案比较
应用程序设计因素 |
高可靠性、低性能方案 |
高性能、低可靠性方案 |
---|---|---|
传送模式 |
持久性消息 |
非持久性消息 |
使用事务 |
事务会话 |
非事务 |
确认模式 |
AUTO_ACKNOWLEDGE 或 CLIENT_ACKNOWLEDGE |
DUPS_OK_ACKNOWLEDGE |
长期/非长期订阅 |
长期订阅 |
非长期订阅 |
使用选择器 |
消息过滤 |
无消息过滤 |
消息大小 |
大量的小消息 |
少量的大消息 |
消息主体类型 |
复杂主体类型 |
简单主体类型 |
持久性消息能够在代理发生故障的情况下保证消息的传送。代理会将消息存储在持久性存储库中,直到所有预期的使用方都确认已使用消息为止。
代理处理持久性消息比处理非持久性消息要慢,原因如下:
代理必须可靠地存储持久性消息,以使其即使在代理出现故障时也不会丢失。
代理必须确认它所收到的每一条持久性消息。如果生成消息的方法正常返回,则说明向代理的传送成功。
根据客户端确认模式的不同,代理可能需要确认使用方客户端对持久性消息的确认。
对于队列和具有长期订户的主题而言,代理处理非持久性消息的性能要高大约 40%。 这些结果是在使用 10k 大小的消息和 AUTO_ACKNOWLEDGE 模式的情况下得到的。
事务是一种保证,保证在一个事务会话中生成和使用的所有消息将作为一个单元进行处理或不进行处理(回滚)。
Message Queue 支持本地事务和分布式事务。
消息在事务会话中的生成或确认比在非事务会话中要慢,原因如下:
其他信息必须随每个生成的消息存储。
在某些情况下,会存储事务中通常不该存储的消息(例如,传送至没有订阅的主题目的地的持久性消息通常应该删除,但在事务开始时,却没有关于订阅方面的信息)。
提交事务时,必须存储并处理该事务中有关消息的使用和确认的信息。
确保可靠传送 JMS 消息的一种机制是,客户端确认使用了由 Message Queue 代理传送给它的消息。
如果在客户端尚未确认消息之前就关闭了会话,或者如果代理在处理确认之前发生了故障,代理将重新传送该消息,并设置一个 JMSRedelivered 标志。
对于非事务会话,客户端可以选择三种确认模式之一,这三种模式有其各自的性能特性:
AUTO_ACKNOWLEDGE。使用方处理消息后,系统会自动确认该消息。此模式可确保在提供者发生故障后至少重新传送一次消息。
CLIENT_ACKNOWLEDGE。应用程序控制确认消息的时间点。该会话中自上次确认以来处理的所有消息都将被确认。如果代理在处理一组确认时发生故障,则该组中的一个或多个消息将被重新传送。
DUPS_OK_ACKNOWLEDGE。此模式指示系统以一种惰性方式确认消息。在提供者发生故障后,可以重新传送多条消息。
(CLIENT_ACKNOWLEDGE 模式的用法与事务的用法类似,不同之处在于:它不能保证当提供者在处理过程中发生故障时,所有确认都将一起处理。)
确认模式影响性能的原因如下:
AUTO_ACKNOWLEDGE 和 CLIENT_ACKNOWLEDGE 模式需要在代理与客户端之间有额外的控制消息。额外的控制消息会带来额外的处理开销,并干扰 JMS 有效负荷消息,从而导致处理延迟。
在 AUTO_ACKNOWLEDGE 和 CLIENT_ACKNOWLEDGE 模式中,客户端必须等到代理确认它已处理客户端的确认后,才能使用其他消息。(这种代理确认可确保代理不会无意地重新传送这些消息。)
Message Queue 持久性存储库必须用使用方收到的所有持久性消息的确认信息进行更新,因而降低了性能。
主题目的地的订户可以归为两类,即长期订阅的订户和非长期订阅的订户。
长期订阅的可靠性较高,但吞吐量较低,原因如下:
Message Queue 消息服务必须永久地存储指定给每个长期订阅的消息的列表,以便当代理发生故障时,该列表可以在恢复之后使用。
长期订阅的持久性消息是永久存储的,以便当代理发生故障时,消息仍可以在恢复后传送(只要相应的使用方处于活动状态)。与此相反,非长期订阅的持久性消息不是永久存储的(如果代理发生故障,相应的使用方的连接就会断开,消息不再被传送)。
我们比较了两种情况下的长期订户和非长期订户的性能:10k 大小的持久性和非持久性消息。两种情况下都使用 AUTO_ACKNOWLEDGE 确认模式。我们发现只有在持久性消息情况下,才会有性能影响,使长期订阅减慢了大约 30%。
应用程序的开发者常常需要将消息组的目标定为特定使用方。实现此任务的方法有两种:将每组消息分别指向一个唯一的物理目的地;或者是仅使用单个物理目的地,并为每个使用方注册一个或多个选择器。
选择器是一种只请求特定消息的字符串,这些消息的属性值应该与传送到特定使用方的字符串匹配。例如,选择器 NumberOfOrders >1 仅传送 NumberOfOrders 属性值为 2 或更大值的消息。
创建带有选择器的使用方会降低性能(与使用多个物理目的地相比),因为处理每个消息时需要进行额外的处理。如果使用选择器,则必须对它进行解析,以使它与将来的消息匹配。另外,路由每个消息时,都必须检索该消息的属性,并与选择器进行比较。但是,使用选择器为消息传送应用程序提供了更大的灵活性。
消息大小会影响性能,因为从生成方客户端到代理以及从代理到使用方客户端之间必须传递更多的数据,并且对于持久性消息,必须存储更大的消息。
但是,通过将多个较小的消息组织成一个消息在一批中传送,对各个消息的路由和处理就可以变得尽可能地简单,从而获得总体性能的提高。此时,就会丢失有关各个消息的状态的信息。
在我们的测试中,比较了将消息发送至队列目的地并使用 AUTO_ACKNOWLEDGE 确认模式时,1k、10k 和 100k 大小的消息的吞吐量(KB/秒),我们发现对于 1k 的消息,非持久性消息传送比持久性消息传送快大约 50%,对于 10k 的消息快大约 20%,而对于 100k 的消息快大约 5%。消息大小对性能的影响很大,对于持久性消息和非持久性消息而言都是如此。传送 100k 的消息比传送 10k 消息快大约 10 倍,而传送 10k 消息比传送 1k 消息快大约 5 倍。
JMS 支持五种消息主体类型,下面大致按复杂性顺序显示这些类型:
BytesMessage 包含一组字节,格式由应用程序确定。
TextMessage 是一种简单的 Java 字符串。
StreamMessage 包含 Java 基元值流。
MapMessage 包含一组名称/值对。
ObjectMessage 包含 Java 序列化对象。
尽管通常情况下消息类型是由应用程序的需要所决定的,但较复杂的类型(MapMessage 和 ObjectMessage)会增加性能成本:对数据进行序列化和反序列化的成本。性能成本取决于数据的简单或复杂程度。
消息传送应用程序的性能不但受应用程序设计的影响,而且还受执行消息路由和传送的消息服务的影响。
以下各节讨论影响性能的各个消息服务因素。了解这些因素的影响对于评估消息服务以及诊断并解决在部署的应用程序中可能发生的性能瓶颈来说至关重要。
Message Queue 服务中影响性能的最重要的因素有:
以下各节讲述其中的每个因素对消息传送性能所产生的影响。
对于 Message Queue 代理和客户端应用程序而言,CPU 处理速度和可用内存都是消息服务性能的主要决定因素。 大多数软件限制都可以通过增加处理能力来消除,而添加内存则可以同时提高处理速度和容量。但是,仅通过升级硬件来克服瓶颈通常过于昂贵。
由于不同操作系统的效率不同,因此即使硬件平台相同,性能也会各不相同。例如,操作系统使用的线程模型会对代理可以支持的并发连接数产生重要影响。在所有硬件都相同的情况下,Solaris 通常比 Linux 快,而后者通常又比 Windows 快。
代理是受主机 JVM 支持并运行在其中的一种 Java 进程。因此,JVM 处理是决定代理路由和传送消息的速度和效率的重要因素。
特别是 JVM 的内存资源管理至关重要。必须为 JVM 分配足够的内存以适应不断增大的内存负载。另外,JVM 将定期回收未使用的内存,而这种内存回收会延迟消息的处理。JVM 内存堆越大,内存回收过程中可能遇到的延迟就越长。
客户端和代理之间的连接数和速度可能影响消息服务可以处理的消息数以及消息的传送速度。
对代理的所有访问都是通过连接进行的。对并发连接数的任何限制都会影响可以同时使用代理的生成方或使用方客户端的数目。
与代理的连接数通常受可用线程数的限制。可以对 Message Queue 进行配置以支持专用线程模型或共享线程模型(请参见线程池管理)。
专用线程模型的速度非常快,因为每个连接都有专用的线程,但是连接数目受可用线程数的限制(每个连接都有一个输入线程和一个输出线程)。共享线程模型对连接数不加任何限制,但是在大量连接之间共享线程会导致明显的开销和吞吐量延迟,特别是当这些连接都很繁忙时。
Message Queue 软件允许客户端使用各种低级别的传输协议与代理进行通信。Message Queue 支持连接服务中所述的连接服务(及相应协议)。
协议的选择基于应用程序的要求(加密、可通过防火墙访问等),但是所作的选择会影响总体性能。
我们的测试比较了两种情况下的 TCP 和 SSL 吞吐量:一个高可靠性方案(将 1k 大小的持久性消息发送至长期订阅主题目的地,并使用 AUTO_ACKNOWLEDGE 确认模式)和一个高性能方案(将 1k 大小的非持久性消息发送至非长期订阅主题目的地,并使用 DUPS_OK_ACKNOWLEDGE 确认模式)。
总的说来,我们发现在高可靠性情况下协议具有较小影响。这可能是因为在高可靠性情况下所需的持久性开销在限制吞吐量方面是比协议速度更重要的因素。另外:
TCP 提供与代理通信的最快方法。
SSL 在收发消息时比 TCP 要慢 50% 到 70%(对于持久性消息是 50%,对于非持久性消息则接近 70%)。另外,SSL 在建立初始连接时比较慢(可能需要数秒钟),因为客户端和代理(在使用 HTTPS 的情况下则为 Web 服务器)需要建立私钥,以供加密要传输的数据时使用。性能的下降源自于加密和解密每个低级别 TCP 包时所需的额外处理。
HTTP 比 TCP 或 SSL 都要慢。它使用 Servlet,该 Servlet 在 Web 服务器上作为客户端与代理之间的代理运行。封装 HTTP 请求中的包以及消息经过两个跃点(客户端到 Servlet,Servlet 到代理)后才到达代理的要求均涉及性能开销。
HTTPS 比 HTTP 慢是因为需要额外的开销以加密客户端和 Servlet 之间以及 Servlet 和代理之间的包。
Message Queue 消息服务可以作为单个代理实现,也可以作为多个互相连接的代理实例所组成的群集实现。
随着连接到代理的客户端数量以及所传送的消息数量的不断增加,代理最终将超出资源限制(例如文件描述符、线程和内存限制)。要适应不断增加的负载,方法之一是将更多的代理实例添加到 Message Queue 消息服务,从而将客户端连接以及消息路由和传送分布到多个代理。
通常,如果客户端(特别是消息生成方客户端)均匀地分布在群集中,则这种调整最为有效。由于在群集中的代理之间传送消息涉及到开销,因此对于连接数有限或消息传送速率有限的群集而言,其性能可能会比单个代理要低。
您也可以使用代理群集来优化网络带宽。例如,您可能希望在群集内的一组远程代理之间使用低速的长途网络链路,而在客户端与其各自的代理实例之间使用高速链接进行连接。
有关群集的详细信息,请参见第 9 章,使用代理群集。
代理可能需要处理的消息吞吐量是代理所支持的消息传送应用程序的使用模式的一个函数。但是,代理在以下资源上有限:内存、CPU 周期等。因此,代理可能会在无响应或不稳定的位置发生崩溃。
Message Queue 消息代理具有管理内存资源并防止代理用尽内存的内部机制。这些机制包括可以配置代理或其各自物理目的地可以拥有的消息数或消息字节数的限制,以及当达到物理目的地限制时可以实施的一组行为。
通过仔细的监视和调整,这些可配置机制可以用于平衡消息的内流和外流,使得不会发生系统过载。尽管这些机制会造成开销并限制消息的吞吐量,但它们可以维护操作的完整性。
Message Queue 既支持基于文件的持久性模块,也支持基于 JDBC 的持久性模块。基于文件的持久性使用单独的文件存储持久性数据。基于 JDBC 的持久性使用 Java 数据库连接 (Java Database Connectivity, JDBC™) 接口,并需要符合 JDBC 的数据存储库。基于文件的持久性通常比基于 JDBC 的持久性快;但是,某些用户对于符合 JDBC 的存储库所提供的冗余和管理控制更感兴趣。
如果是基于文件的持久性,您可以通过指定让持久性操作将内存中的状态与数据存储库同步,来最大限度地提高可靠性。这有助于消除因系统崩溃而导致的数据丢失,但代价是性能的下降。
Message Queue 客户端运行时环境提供客户端应用程序及其与 Message Queue 消息服务的接口。它支持客户端向物理目的地发送消息以及接收来自这些目的地的消息所需的所有操作。客户端运行时环境是可配置的(通过设置连接工厂属性值),您可以控制其行为的各个方面(例如连接流度量、使用方流限制和连接流限制),从而提高性能和消息吞吐量。有关这些功能和用来配置这些功能的属性的详细信息,请参见客户端运行时环境消息流调整。