IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope: Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  WebSphere | Java technology  >

使高性能消息驱动 Bean 和 WebSphere Application Server 发挥最大功效

使应用程序组件协同工作以达到可能的最佳性能

developerWorks
文档选项

未显示需要 JavaScript 的文档选项


级别: 中级

Ian Parkinson (PARKIW@uk.ibm.com), IT 专家, IBM

2005 年 8 月 31 日

当高性能成为优先考虑的事项时,确保正确配置应用程序服务器和 JMS 提供程序非常重要。本文探讨了 IBM® WebSphere® Application Server 中可用的配置选项,并通过一些常规经验法则和最佳实践介绍了如何处理潜在的限制与性能问题。

引言

Java™ 2 Enterprise Edition (J2EE™) 环境和 IBM WebSphere Application Server 的消息驱动 Bean (MDB) 特性支持通过 Java Message Service (JMS) 处理由消息到达而触发的操作。当高性能成为优先考虑的事项时,确保正确配置应用程序服务器和 JMS 提供程序非常重要。本文以 MDB 为重点,将探讨 WebSphere Application Server 管理员可用的配置选项,并讨论这些选项如何影响应用程序服务器的行为。我们还将介绍您可能遇到的一些限制和问题,以及如何检测和处理它们,同时讨论有关 MDB 负载注入器的一些最佳实践。

本文假定您熟悉一般的 WebSphere Application Server 配置和部署。





回页首


打好基础

J2EE 环境和 JMS 提供了许多服务质量 (QoS) 保证。例如:

  • JMS 提供了一种持久性传递模式,确保消息在发送过程中不会丢失。
  • J2EE 提供了一些事务,通过这些事务可以将一系列操作(持久性消息接收、数据库更新,等等)划分为单个原子操作。原子性的保证意味着,要么所有这些操作都将出现,要么一个操作也不出现,从而确保整个数据的完整性。

这些 QoS 通常保证具有与其关联的重要性能开销,因此选择应用程序可接受的最低级别的 QoS 非常重要。

通常,持久性和事务性是同时并存的。但是,有了 MDB 触发的应用程序,事务模式就由 MDB 的部署描述符选择,而消息持久性由发送此消息的应用程序选择。如果 MDB 确实对事务性(或持久性)没有要求,则请求客户机必须知道要发送非持久性消息。

如果 MDB 不必为事务性,则请检查 MDB 的部署描述符以确保它没有为每条消息启动事务。这是由 Bean 的事务类型属性控制的:Container 的事务类型将使用事务,而 Bean 的事务类型将不使用事务。





回页首


并发与池

在许多高性能场景中,由于消息排列不很重要,因此在这些场景中,我们可以配置 WebSphere Application Server 来并发处理许多消息。这在多处理器系统中是一项特别重要的技术,(原则上)可在其中进行并发处理而无需降低响应时间。

为了在使用并发消息传递时优化性能,我们必须将注意力转向应用程序服务器托管的许多对象池。

MDB 的 onMessage 方法是在 MessageListener 线程上执行的;该线程在 MDB 实例与侦听器端口之间池化和重用。必须配置此 MessageListener 线程池,以使足够的并发达到性能要求。另外,每个侦听器端口都提供一个可配置的属性 Maximum Sessions,该属性会限制这个特定的侦听器端口的并发。(Maximum Sessions 属性的缺省值为 1,其阻止在侦听器端口上进行并发处理。因此,如果想要同时处理多条消息,就需要更改这一限制。)

侦听器端口与 JMS ConnectionFactory 相关联,而 JMS ConnectionFactory 本身提供可优化的连接池。每一个有效的连接维护一个会话池,会话池也可通过 ConnectionFactory 的属性进行优化。每一个活动侦听器端口将使用一个连接;当 MessageListener 线程代表侦听器端口传递消息时,它将使用此侦听器的连接所拥有的会话池中的会话。

存在一系列池和限制,其中每个池必须足够大,以支持上述线程池。MessageListener 线程池应该比任何侦听器端口最大会话值大。具体多大将取决于系统在负载下的工作方式。ConnectionFactory 的连接池应该大于侦听器端口的数,还应该能够为任何使用该 ConnectionFactory 的应用程序提供容量。会话池应该大到能包含使用连接的任何侦听器端口的最大会话。

使用单独的 ConnectionFactories 适当地设置侦听器端口和应用程序组件。何时使用不同设置的一个很好的例子是这样一种常见的情况:MDB 的 onMessage 方法获得自己的连接和会话来发送响应。在这种情况下,侦听器端口的 ConnectionFactory 只需要支持一个连接,但是它必须允许每个并发 MDB 都有一个会话。相反,onMessage 使用的 ConnectionFactory 应该允许多个连接,每个连接一个会话。

我们可以构建并发 MDB 使用的行为的简单数学模型:

  • 如果我们希望在正常环境下花 t 秒来完成一个事务,而且我们要求每秒的目标吞吐量为 n 个事务,则我们将需要 nt 个并发线程来实现我们的目标。例如,如果完成每个事务要花 0.1 秒,而且我们每秒需要完成 100 个事务,则我们需要 0.1 * 100 = 10 个并发线程。这间接表明了最大会话设置值以及与侦听器端口的连接关联的会话池的大小。

  • 如果我们的事务是 CPU 限制的,则生产并发数可能最多比可用处理器数多两三倍。当并发数增加到超过该值时,由于线程争用 CPU 时间,所以事务响应时间将增加,因而实际上并未带来任何吞吐量方面的改进。另一方面,如果事务是 I/O 限制的(或者可能将工作卸载到其他某个服务器),则我们可能需要采用非常大的并发数,以达到相当高的吞吐量。





回页首


最大消息

当可以将消息传递给 MDB 时,应用程序服务器必须设置调用 onMessage 方法的环境。这可能涉及检查安全凭据、建立事务上下文和从池中获得 MDB。同样,在传递了消息之后,必须提交事务,这可能涉及调用多个资源。自然,这涉及一些性能开销,因此 WebSphere Application Server 提供了一个选项,在多个消息传递之间分担开销。

在侦听器端口设置最大消息值及最大会话并发数。当设置的最大消息大于缺省值 1 时,可以在同一上下文中多次调用 MDB 实例的 onMessage 方法。如果事务时间短,则可以大大提高吞吐量。

当然,开销——因而通过此设置获得改进——取决于 MDB 的性质。如果 MDB 是非事务性的,则通过设置此属性获得的好处将少得多。

不利的方面是,这将增加延时。到达队列的消息可能会发现自己排在一批消息的末尾,在这种情况下,对它的处理将延迟。粗略地说,如果每个事务耗时 t,而我们有 m 个最大消息,则事务平均需要花费 1/2 (m+1)t 的时间完成;在最坏的情况下,事务需要花费 mt 的时间完成。批可能比最大消息值小:如果队列中没有足够的消息,将立即传递(和提交)“未完成”的批,不需要等待足够的消息来完成该批。当然,如果负载超过了系统的容量,则不管最大消息的值是多少,消息都将排队等候。

此外,我们必须考虑 MDB 的 onMessage 方法(和任何委托方法)的性质。如果 MDB 以事务的方式锁定资源(例如,它可以锁定数据库行),则在批结束之前,这些资源将保持锁定。虽然在通常的情况下,这不会阻止后面的消息访问这些资源,但是它将延迟其他事务对这些资源的访问,因而可能形成瓶颈。

对于托管连接池,需要进行类似的考虑:在批(及其事务)完成之前,(事务)连接将不返回到池。具体的例子就是 JMS 连接和会话对象及其相关池。设想我们的 Bean 通过 JMS 发送一条响应消息,这样它将首先获得连接,然后进行会话。不将 JMS 连接视为事务资源,所以只要它关闭,就将返回到连接池。但是将会话视为事务性的,所以直到事务完成,它才返回到连接的会话池。当以后在同一批中进行调用时,如果 MDB 恰好从连接池中获得相同的连接,它将获得相同的会话。如果以后从连接池中获得不同的连接,则它自然将获得不同的会话。同样地,如果将连接提供给 MDB 或事务外的其他组件,则连接的会话池必须足够大,以便容纳新的组件的会话及未完成的事务持有的任何会话。

从最大消息数增加中获得的任何好处都遵循收益递减的法则:如果:

  • 启动新事务、分派 MDB 和执行事务清除的每次一批的开销为 b
  • 执行 onMessage 方法的每条消息的成本(和任何其他的每条消息的开销,如准备/提交每条消息获得每个事务资源)为 t,并且
  • 将最大消息属性设置为 m

则每条消息的平均开销为 (1/m)b + t

假设 t=0,这是不可能的,则将最大消息属性从 1 增加到 2 获得的理论最大改进为 100%;从 2 增加到 3 外加 50%;从 3 增加到 4 外加 33%,依此类推。随着批大小的增加,有望从增加批大小中获得的改进在逐步减少。除了非常短的事务之外,似乎不可能将此值设置为大于 4 或 5。





回页首


向上扩展:调度程序线程

要获得高的事务吞吐量率,可能需要考虑在队列、队列管理器和应用程序服务器实例之间分配工作。原则上,在每个系统中使用一个队列、一个队列管理器和一个应用程序服务器应该就足够了,但在实践中,对于有些设计决策,可能值得试验多个实例。

IBM WebSphere MQ MDB 实现使用单线程来分派输入队列中的传入消息。在多处理器系统中,这在理论上可能成为一个瓶颈:因为调度工作不能在处理器之间分担,单个处理器的速度对可以处理的工作的数量有影响。调度程序线程分派消息的速度应该能够比单个应用程序实际处理的速度快几倍,但是对于多处理器系统中的短事务,作用可能是显而易见的。

解决这个问题的方法是设置多个应用程序服务器。然后,每个应用程序服务器都将使用自己的调度程序线程在其内执行自己的分派。遗憾的是,难以评估这种系统中单个调度程序线程支持的并发数。这主要取决于每个事务的长度,同时还取决于以下几个方面:是否在 MDB 中指定选择器(以及选择器的复杂性)、MDB 是否是事务性的,以及正在使用的连接模式(绑定或客户机)。

知道何时达到调度程序线程的限制非常有用。这很容易用实验方法确定:如果我们将并发级别增加到没有影响,而仍然让 CPU 周期保持空闲,则我们要么达到调度程序的限制,要么并发事务正在争用在一定限制条件下运行的资源。后一种情况可以通过在增加并发数时研究 MDB 的方法的响应时间来排除——为此,我们使用 WebSphere Application 附带的 IBM Tivoli® Performance Viewer。





回页首


向上扩展:源队列

当多个 Java 虚拟机(JVM)尝试从同一队列中传递时(如上所述,如果使用多个应用程序服务器,或者正在访问多个应用程序服务器中的中央队列管理器上托管的队列,则可能发生这种情况),WebSphere MQ 中的 MDB 支持有已知的设计限制。在实际从队列中删除消息之前,所有的调度程序都有可能分派给定的并进行事务设置。 因此我们可以在每个调度程序上执行所有的设置工作,而实际上只让一个调度程序继续传递消息。随着调度程序数的增长,一个调度程序将消息“丢失到”某些其他调度程序的可能性也在增加,而且(至少在理论上)这个问题可能愈演愈烈。

我们可以使用 Tivoli Performance Viewer 的最新版本来识别这个问题。如果报告的传递给 MDB 的数目少于报告的调用 MDB 的远程方法的数目,则指示 MDB 实例正在争用消息,这是一件好事。但是,需要了解 Tivoli Performance Viewer 中报告的这些字段的含义,并知道这些含义在一些版本之间可能有所改变。

虽然这种情况在该字段中出现过,但是很少观察到。如果遇到,可以采用如下方法解决:每个应用程序服务器使用一个单独的队列,并且利用分布机制来分散队列之间的请求。一种最简单的这样的机制是,让请求客户机显式选择要将它们的消息发送到的队列。但是在通常情况下,一种更好的解决此问题的方法是使用 MQ 集群技术,这将为每个应用程序服务器提供自己的队列管理器。请求客户机将通过一些本地网关队列管理器发送消息,本地网关队列管理器将在宿主队列管理器之间分布消息。如果采用这种方法,则不限制我们自己使用单网关队列管理器可能非常重要,因为在队列管理器中分布消息的处理也是单线程的,每个队列都保持消息次序。

为每个应用程序服务器实例提供一个队列管理器还有另一个好处,即可以通过应用程序服务器共同托管队列服务器,这可以提高性能和健壮性。





回页首


消息注入

在测试高性能系统时,常见的障碍是消息注入。事实证明,将足够的工作注入系统来全面测试系统是相当困难的。在考虑消息注入时,请记住以下最佳实践:

  • 从您尝试加压的系统外远程运行注入器应用程序。构建消息(特别是 JMS 消息)和解析消息通常是开销相当大的过程,因此最好在不同的计算机上运行这些注入器应用程序。

  • 将注入器应用程序连接(使用绑定)到本地队列管理器,并且使用 MQ 网络将消息移到测试系统中。与绑定连接相比,MQ 客户机协议可能有点“chatty”且比较慢;另一方面,队列管理器协议的 MQ 队列管理器经过优化,用于移动大量的消息。

  • 如果您注入的是持久性消息,则以事务的方式发送这些消息,并且每次提交几条消息,可能获得更好的性能。这归因于 WebSphere MQ 访问磁盘的方式:实质上,MQ 只能在磁盘的相关部分转过磁盘头时才响应 commit()。将持久性消息发送到事务之外,或者在每条消息之后提交,都可能由于磁盘访问而导致延迟。这同样适用于读取响应消息。

  • 如果可以同时使用多个线程来注入(或接收)消息,您将获得更好的性能。对于绑定连接,您将看到每个处理器或许有 4 到 5 个线程。对于客户机连接,这个值要大得多。

  • 如果注入器可以以一定的速率引入消息,则您将获得更多的控制。相反,如果您只是尽可能快地注入消息,则难以查看系统的运行情况。如果存在任何瓶颈,队列将很快堆积起来(因而产生重大的故障)。

  • 如果您测试的是请求/响应系统,则考虑让注入器只有在接收到响应后才发送新的请求。这应该确保注入器处于一定的负荷状态而不会超负荷,并且使注入器的运行时间延长,而不必担心队列堆积。为了确保系统始终有工作可做,可以安排较多(或许有 50 到 100 个)的请求数。

如果发现不能以所需的速度注入消息,但是感到系统中仍然有能力,则通常可以 GET(禁用)请求队列,预加载带有合适数量的消息的队列,然后 GET(启用)该队列。如果安排系统处理该工作负荷的时间,则可以评估最大的事务率。

在执行这种测试时,请记住许多组件将在测试的过程中自优化。特别是,MQ 的行为经常比测试运行时好两三倍,因为内部对象初始化了。在多次运行之后,JVM 同样可以更好地执行特定的代码段,这主要是因为 JIT 编译,同时也因为执行路径分析。因此,包括“预热阶段”非常重要,通常在进行测试之前用数千个事务预热。





回页首


结束语

WebSphere 软件可以轻松地支持大规模性能关键型的部署。优化这些系统非常重要,如果没有充分理解系统行为的各个组件(包括数据层、中间件和应用程序组件),可能会遇到很大的困难。希望本文能够帮助您理解消息驱动 J2EE 应用程序的行为,并且为您提供一些解决可能出现的性能问题的思路。



参考资料

学习

获得产品和技术


关于作者

Ian Parkinson 是位于英国 Hursley 的 IBM Software Group 的 Application and Integration Middleware (AIM) 部门的 IT 专家。




对本文的评价










回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款