应用程序的设计决策对消息传送的总体性能有很大影响。
对性能影响最大的主要是那些影响消息传送的可靠性因素。其中包括下列因素:
其他影响性能的应用程序设计因素有:
接下来的几节讲述其中的每个因素对消息传送性能所产生的影响。通常,应当在性能与可靠性之间进行权衡。提高可靠性的因素可能会导致性能降低。
表 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)会增加性能成本:对数据进行序列化和反序列化的成本。性能成本取决于数据的简单或复杂程度。