内容


将 JMS 连接池与 WebSphere Application Server 和 WebSphere MQ 一起使用,第 1 部分

Comments

引言

为提高性能,IBM® WebSphere® Application Server 维护了一个到 JMS 提供程序的连接池。当一个应用程序创建 JMS 连接时,应用服务器将查看空闲连接池中是否已经存在连接——如果池中存在连接,它将返回给应用程序。否则,将会创建一个新连接。但空闲连接池的实际工作原理是什么?

这一由两部分组成的系列文章的第 1 部分将介绍如何使用空闲连接池,如何管理池内容,以及池的各种属性是如何协作的。

JMS 连接池

总的来说,连接池是一个到 JMS 提供程序的空闲连接池。JMS 包含连接工厂概念,可以使用连接工厂创建到 JMS 提供程序的连接。WebSphere Application Server 对连接工厂可以创建的连接数有一个限制,该限制由连接工厂的最大连接属性指定。此属性的缺省值为 10,这意味着从连接工厂一次最多只能创建 10 个连接。

每个工厂都有一个关联的空闲连接池。在应用服务器启动时,连接池是空的。一个工厂的空闲池中可以存在的最大连接数也由最大连接属性指定。图 1 显示了定义有三个 JMS 连接工厂的应用服务器上的 JMS 连接池:

图 1. JMS 连接池
图 1. JMS 连接池
图 1. JMS 连接池

如何使用连接池

在应用服务器内部运行的应用程序使用工厂创建连接时(例如,通过调用 connectionFactory.createConnection()),WebSphere Application Server Connection Manager 将尝试从此工厂的空闲池获得连接,并将其返回给应用程序。如果池中没有任何空闲连接,并且从此工厂创建的连接数没有达到工厂的最大连接属性指定的限制,Connection Manager 将为要使用的应用程序创建新连接。

如果应用程序尝试创建连接,但是从此工厂创建的连接数已等于工厂的最大连接属性指定的限制,则会发生什么情况?这个问题问得好!

在这种情况下,应用程序将等待连接重新可用(放回空闲池中)。应用程序等待的时间是在连接池的连接超时属性中指定的,缺省值为 180 秒(3 分钟)。如果在这 3 分钟内将连接放回空闲池,则 Connection Manager 将立即从池中再次取出它,并将其传递给应用程序。不过,如果超过超时段,则会引发 ConnectionWaitTimeoutException。图 2 显示了如何从空闲连接池检索 JMS 连接:

图 2. 如何从空闲连接池检索 JMS 连接
图 2. 如何从空闲连接池检索 JMS 连接
图 2. 如何从空闲连接池检索 JMS 连接

应用程序完成连接并通过调用 connection.close() 将其关闭后,该连接实际上处于打开状态,并返回给空闲池,这样另一个应用程序就可以重新使用它。因此,即使没有 JMS 应用程序在应用服务器上运行,也可以打开 WebSphere Application Server 和 JMS 提供程序之间的连接。

消息驱动的 Bean 侦听器端口如何使用连接池

假设您在将 WebSphere MQ 用作 JMS 提供程序的 WebSphere Application Server V6.1 Base 系统上部署了消息驱动的 Bean (MDB)。MDB 是在称为 MDBListener1 的侦听器端口上部署的,该端口正在使用连接工厂 jms/CF1。此连接工厂将最大连接属性设置为 2,这意味着一次只能从此工厂创建两个连接。

当侦听器端口启动时,它尝试使用 jms/CF1 连接工厂创建到 WebSphere MQ 的连接。为此,它从 Connection Manager 请求连接。由于这是第一次使用 jms/CF1 连接工厂,jms/CF1 空闲连接池中没有任何连接,所以 Connection Manager 将创建新连接 c1。此连接将存在于侦听器端口的整个生命周期。

图 3. 使用 jms/CF1 的连接 c1 的 MDBListener1
图 3. 使用 jms/CF1 的连接 c1 的 MDBListener1

如果使用 WebSphere 管理控制台停止侦听器端口,则会发生什么情况?在这种情况下,Connection Manager 将获取连接,并将其放回空闲池。到 WebSphere MQ 的连接仍处于打开状态。

图 4. MDBListener1 停止,并且从 jms/CF1 创建的连接 c1 返回到工厂空闲池
图 4. MDBListener1 停止,并且从 jms/CF1 创建的连接 c1 返回到工厂空闲池

如果重新启动侦听器端口,它会再次请求 Connection Manager,以获得到队列管理器的连接。现在,空闲池中含有连接 c1,这样 Connection Manager 将从该池中取出此连接,并将其提供给侦听器端口。现在,假设您将第二个 MDB 部署到应用服务器,并且 MDB 使用另一个侦听器端口 MDBListener2:

图 5. 两个 MDB 侦听器使用从同一连接工厂创建的连接运行
图 5. 两个 MDB 侦听器使用从同一连接工厂创建的连接运行

然后,假设您尝试启动第三个侦听器端口 (MDBListener3),并且也将其配置为使用 jms/CF1 连接工厂。侦听器端口从 Connection Manager 请求连接,Connection Manager 在空闲池中查找 jms/CF1,并发现空闲池为空。然后检查从 jms/CF1 工厂已创建了多少个连接。由于将 jms/CF1 的最大连接属性设置为 2,并且从此工厂已创建了两个连接,所以 Connection Manager 将等待 3 分钟(连接超时属性的缺省值),以获得变为可用的连接:

图 6. MDBListener3 必须等待从工厂 jms/CF1 创建的连接返回到空闲池
图 6. MDBListener3 必须等待从工厂 jms/CF1 创建的连接返回到空闲池

请考虑如果侦听器端口 MDBListener1 停止,会发生什么情况。其连接 c1 放入 jms/CF1 的空闲池,然后 Connection Manager 检索此连接并将其提供给 MDBListener3:

图 7. MDBListener3 使用 MDBListener1 先前使用的连接启动
图 7. MDBListener3 使用 MDBListener1 先前使用的连接启动

如果您现在尝试重新启动 MDBListener1,则在重新启动之前,必须等待其他侦听端口之一停止。如果运行的侦听器端口在 3 分钟内没有任何一个停止,则 MDBListener1 将收到 ConnectionWaitTimeoutException 并停止。

Enterprise JavaBeans 如何使用连接池

这一次,我们假设将名为 EJB1 的单个 Enterprise JavaBean (EJB) 安装到应用服务器。该 Bean 实现称为 sendMessage() 的方法,其行为方式如下:

  • 通过调用 connectionFactory.createConnection() 从工厂 jms/CF1 创建到 WebSphere MQ 的 JMS 连接。
  • 从连接创建 JMS 会话。
  • 从会话创建消息生产者。
  • 发送消息。
  • 关闭生产者。
  • 关闭会话。
  • 通过调用 connection.close() 关闭连接。

假设工厂 jms/CF1 的空闲池为空。第一次调用 EJB 时,它尝试从工厂 jms/CF1 创建到 WebSphere MQ 的连接。由于工厂的空闲池为空,Connection Manager 将创建新连接,并将其提供给 EJB1:

图 8. 调用 EJB1 的 sendMessage() 方法。通过调用 connectionFactory.createConnection() 创建到 WebSphere MQ 的连接 c1
图 8. 调用 EJB1 的 sendMessage() 方法。通过调用 connectionFactory.createConnection() 创建到 WebSphere MQ 的连接 c1

就在该方法退出之前调用 Connection.close()。Connection Manager 会获得 c1,而不是关闭该连接,并将其放入 jms/CF1 的空闲池:

图 9. sendMessage() 在退出之前调用 connection.close(),这会导致 c1 返回到 jms/CF1 的空闲池
图 9. sendMessage() 在退出之前调用 connection.close(),这会导致 c1 返回到 jms/CF1 的空闲池

下一次调用 sendMessage() 时,connectionFactory.createConnection() 方法将 c1 返回到应用程序。现在,假设同时运行 EJB 的两个实例。当两个实例都调用 sendMessage() 时,将从 jms/CF1 连接工厂创建两个连接:

图 10. EJB1 和 EJB2 同时调用 sendMessage(),并使用从 jms/CF1 创建的连接
图 10. EJB1 和 EJB2 同时调用 sendMessage(),并使用从 jms/CF1 创建的连接

现在,假设创建了 Bean 的第三个实例。当 EJB3 调用 sendMessage() 时,该方法调用 connectionFactory.createConnection() 以便从 jms/CF1 创建连接。不过,当前已从 jms/CF1 创建了两个连接,这等于此工厂的最大连接值。因此,createConnection() 方法将等待 3 分钟(工厂的连接超时属性的值),以获得变为可用的连接:

图 11. EJB 的 sendMessage() 方法必须等待连接返回到 jms/CF1 的空闲连接池
图 11. EJB 的 sendMessage() 方法必须等待连接返回到 jms/CF1 的空闲连接池

如果 EJB1 的 sendMessage() 方法调用 connection.close() 并退出,将会发生什么情况?它使用的连接 c1 将被放回空闲连接池。Connection Manager 然后从空闲池取出连接,并将其提供给 EJB3。然后返回 Bean 对 connectionFactory.createConnection() 的调用,并允许 sendMessage() 方法完成以下操作:

图 12. EJB1 的 sendMessage() 方法退出,Connection Manager 将 c1 提供给 EJB3
图 12. EJB1 的 sendMessage() 方法退出,Connection Manager 将 c1 提供给 EJB3

侦听器端口和 EJBs 使用同一连接池

上面的两个示例显示了侦听器端口和 EJBs 如何单独使用连接池。不过,您可以让侦听器端口和 EJB 在同一应用服务器内部运行,并使用同一连接工厂创建 JMS 连接。上述内容的含义是什么?

需要记住的重要一点是,在侦听器端口和 EJB 之间是共享连接工厂的。例如,假如 MDBListener1 和 EJB1 同时运行,并且二者都使用 jms/CF1 连接工厂,这意味着已经达到工厂的最大连接属性指定的连接限制。如果尝试启动另一个侦听器端口或另一个 EJB 实例,则它们必须等待连接返回到 jms/CF1 的空闲连接池:

图 13. MDBListener2 在启动之前必须等待 c1 或 c2 返回到 jms/CF1
图 13. MDBListener2 在启动之前必须等待 c1 或 c2 返回到 jms/CF1

空闲连接池维护线程

与每个空闲连接池关联的是池维护线程,它监视空闲池,以确保其中的连接仍然有效:

图 14. 具有池维护线程的 JMS 连接池
图 14. 具有池维护线程的 JMS 连接池

如果池维护线程确定需要丢弃空闲池中的连接,则它物理关闭到 JMS 提供程序的连接。

池维护线程的工作原理

池维护线程的行为由连接池的四个属性值确定:

  • 过时超时:打开连接的时间
  • 最小连接数:Connection Manager 在连接工厂的空闲池中保持的最小连接数
  • 获得时间 (Reap time):运行池维护线程的时间间隔
  • 未使用的超时:连接关闭之前在空闲池中保留的时间

缺省情况下,池维护的线程每 180 秒(3 分钟)运行一次,但是通过设置连接池获得时间属性可以更改此值。维护线程在池中查看每个连接,检查连接在池中存放的时间以及连接自创建和最后一次使用以来已经过多长时间。如果连接未使用的时间超过连接池的未使用超时属性指定的值,则维护线程会检查当前空闲池中的连接数。如果该数值大于最小连接值,则 Connection Manager 将关闭该连接。如果连接数等于最小连接数,则该连接将不关闭,并保留在空闲池中。

最小连接属性的缺省值为 1,这意味着 Connection Manager 出于性能原因将总是尝试在空闲池中至少保持一个连接。

未使用超时属性的缺省值为 1800 秒(30 分钟)。在缺省情况下,如果连接放回空闲池中并且至少 30 分钟没有再使用它,并且关闭该连接后,空闲池中至少还保留一个连接,则将关闭该连接。此过程可以防止未使用的连接过时。要关闭此功能,请将此属性设置为零。

如果连接位于空闲池中,并且自创建以来所经过的时间大于连接池的过时超时属性的值,则将关闭该连接,无论该连接自最后一次使用以来已存在多长时间。在缺省情况下,过时超时属性设置为 0,这意味着维护线程从不执行此检查。超过过时超时属性的连接将被丢弃,无论空闲池中还剩多少连接——最小连接属性这时不起作用。下图显示了池维护线程如何清除连接池的内容:

图 15. 池维护线程如何清除连接池
图 15. 池维护线程如何清除连接池
图 15. 池维护线程如何清除连接池

禁用池维护线程

正如您看到的,池维护线程在“醒来”时会执行许多工作,尤其是在连接工厂的空闲池中存在大量的连接时。

例如,假设有以下三个 JMS 连接工厂:jms/CF1、jms/CF2 和 jms/CF3,并且每个连接工厂的最大连接属性值都设置为 10。三个池维护线程每三分钟执行一次,分别扫描 jms/CF1、jms/CF2 和 jms/CF3 的空闲池。如果空闲池有许多连接,则维护线程需要执行许多工作,这会显著地影响性能。

通过将获得时间属性设置为 0,可以禁用个别空闲连接池的池维护线程。禁用维护线程意味着决不会因为i未使用超时过时而关闭连接。不过,如果超过过时超时,它们仍会关闭。应用程序完成连接后,Connection Manager 会进行检查,以确定该连接存在的时间长度,以及该时间长度是否大于过时超时属性指定的值。Connection Manager 关闭该连接,而不是将其返回给空闲池。

过时超时的事务本质

如上所述,过时超时属性指定到 JMS 提供程序的连接在 Connection Manager 关闭它之前保持打开状态的时间长度。其缺省值为 0,这意味着连接不会因为它的连接时间太长而关闭。最好为该属性保留此值,因为在 EJB 内部使用 JMS 时,启用过时超时可以获得事务本质。

在 JMS 中,事务的单元是从 JMS 连接创建的 JMS 会话。它是列入事务(而不是 JMS 连接)的 JMS 会话。由于应用服务器设计的原因,JMS 连接可能会因为超过过时超时而关闭,即使从该连接创建的 JMS 会话涉及一个事务也是如此。关闭 JMS 连接会导致 JMS 会话中的任何未完成的事务工作回滚(如JMS 规范中所述)。不过,应用服务器将不会知道从连接创建的 JMS 会话不再有效。当它尝试使用该会话提交或回滚事务时,将发生 IllegalStateException。

如果希望将过时超时与 EJB 中的 JMS 连接一起使用,则在执行 JMS 操作的 EJB 方法退出之前,确保在JMS 会话中显式提交任何 JMS 工作。

池维护线程

要了解池维护线程如何工作,请返回到 EJB 示例(可能还要使用 MDB 和侦听器端口,因为实际需要的是获得空闲池中连接的方法)。回顾上文可以知道,EJB1 实现称为 sendMessage() 的方法,其工作方式如下:

  • 从工厂 jms/CF1 创建 JMS 连接
  • 从连接创建 JMS 会话
  • 从会话创建消息生产者
  • 发送消息
  • 关闭生产者
  • 关闭会话
  • 关闭连接

连接工厂的配置为:获得时间为缺省值 180 秒(3 分钟)、过时超时为缺省值 0 秒、未使用超时的设置为 300 秒(5分钟)。应用服务器启动后将调用 sendMessage() 方法。该方法使用 jms/CF1 工厂创建连接,并使用该连接发送消息,然后调用 connection.close(),这会导致将 c1 放入空闲池:

图 16. 时间=0 秒,sendMessage() 退出,将 c1 放入空闲池
图 16. 时间=0 秒,sendMessage() 退出,将 c1 放入空闲池

3 分钟后,池维护线程启动,并查看 jms/QCF1 空闲连接池。池中有一个空闲连接 c1,这样维护线程查看将连接放回的时间,并将其与当前时间进行比较。从将连接放入空闲池起已经超过三分钟,这小于 jms/CF1 的未使用超时属性的值。因此,维护线程保留该连接。

三分钟后,池维护线程再次运行。它将发现连接 c1,并确定该连接在池中已存放 360 秒(6 分钟),此时间大于未使用超时的时间,因此 Connection Manager 将关闭该连接:

图 17. 时间=6 分钟,池维护线程运行并关闭 c1,因为已超过该连接的未使用超时限制
图 17. 时间=6 分钟,池维护线程运行并关闭 c1,因为已超过该连接的未使用超时限制

假设现在再次运行 sendMessage()。当应用程序调用 connectionFactory.createConnection() 时,Connection Manager 会创建到 WebSphere MQ 的新连接,因为连接工厂的空闲连接池现在已为空。

此示例显示了维护线程如何使用获得时间和未使用超时属性防止连接过时。您可能要问“过时超时属性的工作原理是什么?”。假设过时超时属性已设置为 300 秒(5 分钟),未使用超时已设置为 0:

图 18. 时间=0 秒,sendMessage() 退出,c1 被放入空闲池
图 18. 时间=0 秒,sendMessage() 退出,c1 被放入空闲池

调用 sendMessage() 方法,并尝试从 jms/CF1 连接工厂创建连接。因为此工厂的空闲池为空,所以 Connection Manager 会创建新的连接 c1,并将其返回给应用程序。当 sendMessage() 调用 connection.close() 时,会将 c1 放回空闲连接池。

三分钟后,池维护线程运行。它在空闲连接池中找到 c1,并检查它创建了多长时间。该连接已存在三分钟,这小于过时超时时间,这样池维护线程将保留该连接,并回到睡眠状态。过一段时间后,将再次调用 sendMessage()。这一次,在调用 connectionFactory.createConnection() 时,Connection Manager 发现 jms/CF1 空闲池中有 c1 可以使用。它从空闲池中取出 c1,并将其提供给应用程序:

图 19. 时间=4 分钟,EJB1 重新使用 c1
图 19. 时间=4 分钟,EJB1 重新使用 c1

sendMessage() 退出时,该连接返回到空闲池。两分钟后,池维护线程再次运行,扫描 jms/CF1 的空闲池的内容,并发现 c1。尽管仅在 120 秒前使用过连接,但是池维护线程会关闭它,因为它存在的时间大于过时超时的值:

图 20. 时间=6 分钟,EJB1 关闭 c1,因为它存在的时间大于过时超时的值(5 分钟)
图 20. 时间=6 分钟,EJB1 关闭 c1,因为它存在的时间大于过时超时的值(5 分钟)

最小连接属性如何影响池维护线程

我们再次以 MDB 为例,假设在应用服务器中部署了两个 MDB,每个 MDB 使用不同的侦听器端口。将每个侦听器端口配置为使用 jms/CF1 连接工厂,该连接工厂的配置为:未使用超时设置为 120 秒(2 分钟)、获得时间设置为 180 秒(3 分钟)、最小连接数设置为 1。

假设 MDBListener1 停止,其连接 c1 放入空闲池。三分钟之后,池维护线程运行,扫描 jms/CF1 的空闲连接池的内容,并发现 c1 在空闲池中存在的时间大于工厂的未使用超时属性的值。

但是,如果丢弃 c1,则在关闭此连接之前,池维护线程会进行检查,以确定池中还保留有多少个连接。由于 c1 是空闲连接池中唯一的连接,所以 Connection Manager 不会关闭它,因为这样会使空闲池中剩余的连接数小于最小连接数的值:

图 21. c1 处于打开状态,以确保空闲池至少包含最小连接数
图 21. c1 处于打开状态,以确保空闲池至少包含最小连接数

现在,假设 MDBListener2 停止。空闲连接池现在包含两个空闲连接:c1 和 c2。三分钟后,池维护线程再次运行。到此时为止,c1 已在空闲连接池中存在了 6 分钟,c2 存在了 3 分钟。

池维护线程检查 c1,发现其在池中存在的时间大于未使用超时的值。线程然后进行检查,以确定空闲池中有多少个连接,将此值与最小连接属性的值进行比较。由于池包含两个连接,并且最小连接设置为 1,所以 Connection Manager 将关闭 c1。

维护线程现在查找 c2。它在空闲连接池中存在的时间也大于未使用超时的值。不过,由于关闭 c2 会使空闲连接池中保留的连接数小于其中的最小连接数,所以 Connection Manager 将保留它:

图 22. c1 和 c2 在空闲池中存在的时间都大于未使用超时的值。不过,这只会关闭 c1,以确保该池至少包含最小连接数
图 22. c1 和 c2 在空闲池中存在的时间都大于未使用超时的值。不过,这只会关闭 c1,以确保该池至少包含最小连接数

结束语

本文阐述了 WebSphere Application Server 为改进性能如何将 JMS 提供程序的空闲连接存储在池中。本文向您介绍了 EJB 应用程序和 MDB 侦听器端口在创建 JMS 连接时如何使用空闲池,当应用程序和侦听器端口完成连接时,这些连接会发生什么情况,以及池维护线程如何清除空闲连接池,以防止 JMS 过时。本文还阐述了许多连接池属性的行为。

第 2 部分将描述高级连接池属性,如何从池中清除过时连接,以及在将 WebSphere MQ 用作 JMS 提供程序时 WebSphere Application Server JMS 连接池如何工作。


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=WebSphere
ArticleID=281811
ArticleTitle=将 JMS 连接池与 WebSphere Application Server 和 WebSphere MQ 一起使用,第 1 部分
publish-date=01142008