使用DefaultMessageListenerContainer连接到WebSphere MQ会反复抛出“Connection closed”

时间:2017-02-28 16:27:50

标签: jms websphere ibm-mq spring-jms

我需要从运行在 WebSphere AS 6.1 应用程序服务器上的Web应用程序连接到远程 z / OS 队列上的WebSphere MQ。在WebSphere AS上,我配置了QueueConnectionFactory和Queue(包含远程队列数据的一部分的对象),大多数设置都设置为默认值 - 我只需要设置队列名称,通道,主机,端口和传输类型是CLIENT。我使用JNDI查找在 Spring 3.2 配置中注入它们:

    <jee:jndi-lookup id="destination" jndi-name="MyMQQueue" expected-type="javax.jms.Queue" />

    <jee:jndi-lookup id="targetConnectionFactory" jndi-name="MyMQQCF" expected-type="javax.jms.QueueConnectionFactory" />

    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"
      p:connectionFactory-ref="targetConnectionFactory"
      p:defaultDestination-ref="destination" />

    <bean id="simpleMessageListener" class="my.own.SimpleMessageListener"/>

    <bean id="msgListenerContainer"
      class="org.springframework.jms.listener.DefaultMessageListenerContainer">
      <property name="connectionFactory" ref="targetConnectionFactory" />
      <property name="destination" ref="destination" />
      <property name="messageListener" ref="simpleMessageListener" />
      <property name="taskExecutor" ref="managedThreadsTaskExecutor" />
      <property name="receiveTimeout" value="5000" />
      <property name="recoveryInterval" value="5000" />
   </bean>

   <bean id="managedThreadsTaskExecutor" class="org.springframework.scheduling.commonj.WorkManagerTaskExecutor">
      <property name="workManagerName" value="wm/default" />
   </bean>

JmsTemplate正确发送和接收(同步)消息。 DefaultMessageListenerContainer,一个异步消息接收器,在WebSphere AS启动期间从MQ队列中读取一些(先前发送的)消息,但很快就会发生阻塞,并开始反复抛出“连接已关闭”异常。在每次这样的场合,它通知我

DefaultMessag W org.springframework.jms.listener.DefaultMessageListenerContainer handleListenerSetupFailure Setup of JMS message listener invoker failed for destination 'queue://myqueue' - trying to recover. Cause: Connection closed
DefaultMessag I org.springframework.jms.listener.DefaultMessageListenerContainer refreshConnectionUntilSuccessful Successfully refreshed JMS Connection

但停止将消息从队列中删除。

挖掘一下Spring代码,我发现在DefaultMessageListenerContainer上设置

<property name="cacheLevel" value="0"/>

解决了这个问题,因为每次发送消息时都会从队列中读取消息。但是,查看到WebSphere MQ的TCP流量,我发现MQCLOSE / MQOPEN命令被重复发送到它,如下所示:

Wireshark captured traffic

这可能意味着连接会持续关闭并重新打开。

任何人都可以建议可能导致缓存无法正常工作的原因,以及是否有一种相对简单的方法来修改Spring代码(例如,扩展DefaultMessageListenerContainer),或者可能在MQ队列连接工厂/队列上设置一些属性,让它工作?

修改

进一步搜索互联网,我找到了以下链接

http://forum.spring.io/forum/spring-projects/integration/jms/89532-defaultmessagelistenercontainer-cachingconnectionfactory-tomcat-and-websphere-mq

似乎描述了Tomcat上发生的类似问题。解决方案是在DefaultMessageListenerContainer上设置某个exceptionListener。但是,尝试在WebSphere上执行此操作会抛出异常“javax.jms.IllegalStateException:Method setExceptionListener not allowed”。根本原因似乎是J2EE 1.4规范禁止在JMS连接上调用setExceptionListener。

https://www.ibm.com/developerworks/library/j-getmess/j-getmess-pdf.pdf

1 个答案:

答案 0 :(得分:1)

似乎设置

<property name="cacheLevel" value="0"/>
DefaultMessageListenerContainer上的

实际上是正确的解决方案。

我通过解释我在Wireshark捕获的TCP流量上看到的MQCLOSE / MQOPEN误导自己,作为重量级连接开放。

首先,管理控制台WebSphere AS 6.1上新创建的连接工厂默认具有JMS连接池(最大大小为10)。通过调试DefaultMessageListenerContainer的基类,AbstractPollingMessageListenerContainer,(特别是方法

protected boolean doReceiveAndExecute(
            Object invoker, Session session, MessageConsumer consumer, TransactionStatus status)

我们发现无论是创建连接的调用,还是从连接创建会话的调用都没有生成TCP流量,只有通过创建消费者才能生成TCP流量(如果我理解正确,则认为是“轻量级操作”) ),尝试从队列接收消息,并关闭消费者。

所以似乎连接是从相应的池中获取的,并且会话也以某种方式“缓存”。

因此,不是通过Spring缓存,缓存似乎是由应用程序服务器在这里完成的。