Tomcat 的 WebSphere MQ 连接池

WebSphere MQ connection pooling with Tomcat

Tomcat有内置的JDBC连接池,可惜没有内置的JMS连接池。

我们正在将遗留 Tomcat Web 应用程序从 WebSphere MQ 版本 6 迁移到版本 7。 不幸的是,连接池已在 WebSphere MQ 7 中删除,如下所述:http://www-01.ibm.com/support/docview.wss?uid=swg21665128

现在Tomcat只用下面的代码配置MQ,怕运行出问题:

        <Resource name="jms/XXXQCF" auth="Container"
            type="com.ibm.mq.jms.MQQueueConnectionFactory" factory="com.ibm.mq.jms.MQQueueConnectionFactoryFactory"
            description="JMS Queue Connection Factory"
            HOST="xxx.com" PORT="1429" CHAN="XXX" TRAN="1"
            QMGR="XXX" />

我们担心的原因是,这在使用 MQ 7 时不会使用池化 JMS 提供程序。有关详细信息,另请参阅 http://activemq.apache.org/jmstemplate-gotchas.html

我们看到的替代解决方案是:

1) Atomikos 的使用

Atomikos 有一个 com.atomikos.jms.AtomikosConnectionFactoryBean 可以用来代替 MQQueueConnectionFactory 但是当我们不需要 XA

时,使用 XA 事务管理器是一个巨大的开销

2) 使用 Spring 的 CachingConnectionFactory

看起来是个不错的解决方案,但遗憾的是我们的遗留应用程序不使用 Spring。 所以我们假设使用 CachingConnectionFactory 需要付出很多努力。

3) 使用 Apache 共享池

看起来也很有前途,但是为 JMS 正确实施它需要一些良好的 JMS 知识

我们的问题:

我想你已经知道答案了。

选项 1:使用 Java EE 应用程序服务器。这有内置的 JMS 池。

选项 2:如果这是一个执行单一作业的简单应用程序(例如放入一个带有 fire and forget 类型的队列),您可以连接到 JMS 提供程序 (qmgr) 并在servlet_init 方法。这意味着遗留代码需要更新。由于 JMS 重新连接属性不会启动重新连接(就我的经验而言),您还需要在出现问题时处理重新连接。

选项 3:当应用程序碰巧比选项 2 复杂一点时,我选择了 Spring 的 CachingConnectionFactory。我有使用 JMSTemplate 的应用程序,有些则没有。

正如 Umapathy 在选项 3 中提出的那样,我们现在选择了使用 Spring 的 CachingConnectionFactory 的方法,即使对于非 Spring 应用程序,它也能很好地工作。您需要做的就是将 Spring Jar 添加到类路径中,并使用 CachingConnectionFactory 包装 MQQueueConnectionFactory。

我们选择创建自己的 Tomcat QueueConnectionFactoryFactory,它使我们能够完全保持原始应用程序代码不变,您只需从 Tomcat 配置文件(如图所示)替换原始 MQ 连接工厂上面的问题)具有以下 XML 定义:

<Resource name="jms/XXXQCF" auth="Container"
          type="org.springframework.jms.connection.CachingConnectionFactory"
          factory="at.rsf4j.core.utilities.RSFCachingMQQueueConnectionFactoryFactory"
          description="JMS Queue Connection Factory"
          HOST="xxx.com" PORT="1429" CHAN="XXX" TRAN="1"
          QMGR="XXX" />

这是 RSFCachingMQQueueConnectionFactoryFactory 的(简化)代码(没有错误检查):

public class RSFCachingMQQueueConnectionFactoryFactory implements ObjectFactory{

public Object getObjectInstance (Object obj, Name name, Context nameCtx, Hashtable<?,?> environment)
    throws NamingException {
            Reference ref = (Reference) obj;
            String beanClassName = ref.getClassName();
            Class<?> beanClass = Class.forName(beanClassName);
            if (CachingConnectionFactory.class.isAssignableFrom(beanClass)){
                MQQueueConnectionFactoryFactory cff = new MQQueueConnectionFactoryFactory();
                Reference mqReference = new Reference(
                        MQQueueConnectionFactory.class.getName());

                Enumeration<RefAddr> allAddrs = ref.getAll();
                while (allAddrs.hasMoreElements()){
                    mqReference.add(allAddrs.nextElement());
                }

                MQQueueConnectionFactory cf = (MQQueueConnectionFactory)cff.getObjectInstance(mqReference, name, nameCtx, environment);
                CachingConnectionFactory ccf = (CachingConnectionFactory)beanClass.newInstance();
                ccf.setTargetConnectionFactory(cf);
                return ccf;
            }
        }