【问题标题】:Spring MDP: polling interval?Spring MDP:轮询间隔?
【发布时间】:2013-01-01 06:50:02
【问题描述】:

一位客户使用这种模式:

  • Apache Camel 和 CXF JMS 接收器
  • 这些内部使用 Spring MDP(消息驱动 POJO)来实现其消息接收器
  • 它们部署在 IBM WebSphere Application Server 7 上
  • 队列管理器是 IBM Websphere MQ 6
  • Spring MDP 使用 JNDI 队列连接工厂绑定到队列管理器 - 支持连接池和会话池

这是一个这样的消息接收器的例子,这个是使用骆驼的:

<bean id="ibmmq" class="org.apache.camel.component.jms.JmsComponent">
    <property name="configuration" ref="jmsConfig"/>
</bean>

<!-- JNDI reference to the queue manager -->
<jee:jndi-lookup id="myTargetConnectionFactory" jndi-name="${mq.queueconnectionfactory}"/>

<bean id="jmsDestResolver" class="org.springframework.jms.support.destination.JndiDestinationResolver"/>

<bean id="myConnectionFactory" class="org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter">
    <property name="targetConnectionFactory" ref="myTargetConnectionFactory"/>
    <property name="username" value="SOME_USER"/>
    <property name="password" value=""/>
</bean>

<bean id="jmsConfig" class="org.apache.camel.component.jms.JmsConfiguration">

    <property name="connectionFactory" ref="${mq.connectionfactorybean}" />        
    <property name="destinationResolver" ref="jmsDestResolver" />        
    <property name="concurrentConsumers" value="1" />
    <property name="maxConcurrentConsumers" value="1" />

    <!--
        NOTE: If we try to use a cache without a transactionManager we get "Connection closed" errors
    -->
    <property name="cacheLevelName" value="CACHE_NONE" />
</bean>

问题:WebSphere MQ 管理员报告了针对队列管理器的大量 MGET() 请求。目前的假设是那些接收者不断地轮询频道以获取新消息。

他们似乎对 MDB(消息驱动 bean)没有这个问题。 MDP 异步实现真的是一种轮询机制吗?如果是这样,有没有办法限制队列管理器的行程?也许增加轮询间隔?任何见解将不胜感激。

【问题讨论】:

  • 您的 JMS 配置看起来不错。那里没有任何东西可以阻止听众作为听众工作。你能发布你的骆驼路线/配置吗?
  • 路线中会针对这个问题寻找什么?回复:“那里没有任何东西会阻止听众作为听众工作。” -- 会知道在哪里可以找到有关 MDP“侦听器”内部的文档。我很担心,因为线程中有一个(可能被误导的)答案:stackoverflow.com/questions/7390286/…。这表明 MDP 侦听器在 QCF 上的行为是轮询 - 与 MDB 相比,可能不是真正的异步侦听器。我希望轮询应用程序不断请求 MGET。
  • 稍后进行一些研究,阅读我的答案。不是 100% 确定,但至少可以从某个地方开始寻找。
  • 谢谢!感谢您抽出宝贵的时间进行研究。我将根据您提供的内容进一步深入研究。

标签: cxf apache-camel ibm-mq websphere-7 spring-jms


【解决方案1】:

我不确定 CXF,但对于 Camel 听众:

JmsConfiguration 中的默认 JMS 使用者似乎是“默认”类型。 意思是,它将实现一个来自 Spring 的 DefaultMessageListenerContainer。

来自Javadoc

使用普通 JMS 客户端 API 的消息侦听器容器变体,特别是 MessageConsumer.receive() 调用循环

接收调用将映射到 MQ GET 调用。

还可以选择指定Simple 类型的消费者,我猜这就是您想要的。

使用普通 JMS 客户端 API 的 MessageConsumer.setMessageListener() 的消息侦听器容器

我不确定,但 Spring 文档指出简单消息侦听器容器不支持 XA 事务。这可能需要考虑,因为您在应用程序服务器中运行。

【讨论】:

    【解决方案2】:

    我们的 Mainframe Q 也有类似的问题。IBM 大型机 q:

    注意应用进程ID作为默认用户 要传递给队列管理器的身份。如果应用程序是 以客户端传输模式运行,则此进程 ID 必须与 服务器机器上的相关授权。如果不同 身份是必需的,那么应用程序应该使用 createConnection(username, password) 方法。

    或者换句话说,IBM 使用 JVM 进程 ID 登录到 mq,除非我们发送适当的凭据。我们使用的是 Spring,所以每次 DefaultMessageListenerContainer 轮询 Q 时,它都必须与它一起发送凭据。我连接了其中一个婴儿和 Bam,像一个魅力一样工作:

    public class CustomConnectionFactory extends CachingConnectionFactory {
    
        private String username;
        private String password;
    
        ...
    
        /**
         * This is the secret sauce. Each time when we make a connection, we send
         * the username/password.
         */
        protected Connection doCreateConnection() throws JMSException {
            return getTargetConnectionFactory().createConnection(this.username, this.password);
        }
    

    我们的大型机更快乐。后来我们切换到分布式 MQ,一切都变得更好了!

    这是我们的最终设置:

    <!-- This hooks us up to the jndi -->
    <jee:jndi-lookup id="telematicsJNDIConnectionFactory" jndi-name="${mq.jndi}" cache="true" lookup-on-startup="true" />
    <!-- The outer wrapper must be TransactionAware, the inner custom one will cache the connection -->
    <bean id="telematicsConnectionFactory" class="org.springframework.jms.connection.TransactionAwareConnectionFactoryProxy">
        <property name="targetConnectionFactory">
            <bean class="cat.dds.tmatic.utils.CustomConnectionFactory">
                <property name="targetConnectionFactory">
                    <ref bean="telematicsJNDIConnectionFactory" />
                </property>
                <property name="username">
                    <value>${mq.user}</value>
                </property>
                <property name="password">
                    <value>${mq.pass}</value>
                </property>
                <property name="sessionCacheSize">
                    <value>10</value>
                </property>
            </bean>
        </property>
        <property name="synchedLocalTransactionAllowed" value="true" />
    </bean>
    

    【讨论】:

    • 这很有意义。我们还反对大型机 MQ。但是我们使用的是 WebSphere JNDI 队列连接工厂,而不是缓存连接工厂。我们还使用 UserCredentialsConnectionFactoryAdapter 来传递凭据(参见上面的配置)。您是否有类似的特征,或者您是否将 MQ 直接连接到队列管理器?
    • 我们使用的是Tomcat,所以图中没有Websphere。但是,我们仍然使用 IBM MQQueueConnectionFactoryFactory 来连接 JNDI。很多罐子问题,但他们解决了......
    • 好的 - 你像我们一样使用 UserCredentialsConnectionFactoryAdapter 吗?我希望这个适配器类似于您的代码(在创建连接时传递凭据)。另外,您的 JNDI 连接工厂是否支持由应用服务器管理的连接池?我认为不会,因为您在 Spring 应用程序中使用了缓存连接工厂。
    • 抱歉含糊不清的答案。我只是在学习这个 MQ 的东西。我们正在使用 TransactionAwareConnectionFactoryProxy 并使用上述连接工厂来添加用户名/密码。我在某个地方找到了一个帖子,但现在找不到了。据我所知,这为我们提供了缓存功能,我希望得到纠正。此类缓存会话,因为它具有 sessionCacheSize 设置器。我们的最终配置见上文...
    猜你喜欢
    • 2016-06-21
    • 1970-01-01
    • 1970-01-01
    • 2017-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-02
    相关资源
    最近更新 更多