远程机器未运行时如何为远程 JMS 队列初始化 ConnectionFactory?

Posted

技术标签:

【中文标题】远程机器未运行时如何为远程 JMS 队列初始化 ConnectionFactory?【英文标题】:How to initialize ConnectionFactory for remote JMS queue when remote machine is not running? 【发布时间】:2010-11-22 08:23:22 【问题描述】:

使用 JBoss 4.0.5、JBossMQ 和 Spring 2.0.8,我尝试将 Spring 配置为实例化依赖于远程 JMS 队列资源的 bean。我遇到的所有示例都依赖于使用 JNDI 来查找诸如远程 ConnectionFactory 对象之类的东西。

我的问题是当试图启动一台将消息放入远程队列的机器时,如果远程机器没有启动,JNDI 查找就会失败,导致部署失败。有没有办法让 Spring 在不阻塞其余部署的情况下继续尝试在后台查找此对象?

【问题讨论】:

【参考方案1】:

如果没有看到您的 spring 配置,很难确定,但假设您使用 Spring 的 JndiObjectFactoryBean 进行 JNDI 查找,那么您可以将 lookupOnStartup 属性设置为 false,这甚至允许上下文启动如果 JNDI 目标不存在。 JNDI 解析将在第一次使用 ConnectionFactory 时完成。

但是,这只是将问题进一步转移到了链条上,因为如果其他一些组件试图在启动时获取 JMS Connection,那么你又回到了你开始的地方。您可以在其他 bean 上使用 lazy-init="true" 属性来防止在部署时发生这种情况,但很容易意外地在您的配置中放置一些东西,这会强制一切初始化。

【讨论】:

【参考方案2】:

你完全正确。我尝试将 lookupOnStartup 设置为 false 和 lazy-init=true 。这只是将问题推迟到第一次尝试使用队列。然后抛出如下异常:

[org.jboss.mq.il.uil2.SocketManager] Failed to handle: org.jboss.mq.il.uil2.msgs.CloseMsg29702787[msgType: m_connectionClosing, msgID: -2147483606, error: null]
java.io.IOException: Client is not connected

此外,看起来再也不会尝试查找了。当具有远程队列的机器重新启动时,随后不会处理任何消息。这确实看起来应该很好地包含在 J2EE 废话的用例范围内,但我运气不佳……感觉它甚至应该是一个已解决的问题。

为了完整起见,以下是我的 Spring 配置的相关部分。

<bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
        <property name="environment">
            <props>
            <prop key="java.naming.provider.url">localhost:1099</prop>
            <prop key="java.naming.factory.url.pkgs">org.jnp.interfaces:org.jboss.naming</prop>
            <prop key="java.naming.factory.initial">org.jnp.interfaces.NamingContextFactory</prop>
            </props>
        </property>
    </bean>

    <bean id="connectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiTemplate">
            <ref bean="jndiTemplate"/>
        </property>
        <property name="jndiName">
            <value>ConnectionFactory</value>
        </property>
    </bean>

    <bean id="remoteJndiTemplate" class="org.springframework.jndi.JndiTemplate" lazy-init="true"> 
        <property name="environment"> 
            <props> 
            <prop key="java.naming.provider.url">jnp://10.0.100.232:1099</prop>
            <prop key="java.naming.factory.url.pkgs">org.jnp.interfaces:org.jboss.naming</prop>
            <prop key="java.naming.factory.initial">org.jnp.interfaces.NamingContextFactory</prop> 
            </props> 
        </property> 
    </bean> 

    <bean id="remoteConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean" lazy-init="true">
        <property name="jndiTemplate" ref="remoteJndiTemplate"/>
        <property name="jndiName" value="ConnectionFactory" />
        <property name="lookupOnStartup" value="false" />
        <property name="proxyInterface" value="javax.jms.ConnectionFactory" />
    </bean>

    <bean id="destinationResolver" class="com.foo.jms.FooDestinationResolver" />

    <bean id="localVoicemailTranscodingDestination" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiTemplate" ref="jndiTemplate"/>
        <property name="jndiName" value="queue/voicemailTranscoding" />
    </bean>

    <bean id="globalVoicemailTranscodingDestination" class="org.springframework.jndi.JndiObjectFactoryBean" lazy-init="true" >
        <property name="jndiTemplate" ref="remoteJndiTemplate" />
        <property name="jndiName" value="queue/globalVoicemailTranscoding" />
    </bean>

    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate" >
        <property name="connectionFactory" ref="connectionFactory"/>
        <property name="defaultDestination" ref="localVoicemailTranscodingDestination" />
    </bean>

    <bean id="remoteJmsTemplate" class="org.springframework.jms.core.JmsTemplate" lazy-init="true">
        <property name="connectionFactory" ref="remoteConnectionFactory"/>
        <property name="destinationResolver" ref="destinationResolver"/>
    </bean>

    <bean id="globalQueueStatus" class="com.foo.bar.recording.GlobalQueueStatus" />

    <!-- Do not deploy this bean for machines other than transcoding machine -->
    <condbean:cond test="$transcoding.server">
        <bean id="voicemailMDPListener"
              class="org.springframework.jms.listener.adapter.MessageListenerAdapter" lazy-init="true">
            <constructor-arg>
                <bean class="com.foo.bar.recording.mdp.VoicemailMDP" lazy-init="true">
                    <property name="manager" ref="vmMgr" />
                </bean>
            </constructor-arg>
        </bean>
    </condbean:cond>

    <bean id="voicemailForwardingMDPListener"
          class="org.springframework.jms.listener.adapter.MessageListenerAdapter" lazy-init="true">
        <constructor-arg>
            <bean class="com.foo.bar.recording.mdp.QueueForwardingMDP" lazy-init="true">
                <property name="queueStatus" ref="globalQueueStatus" />
                <property name="template" ref="remoteJmsTemplate" />
                <property name="remoteDestination" ref="globalVoicemailTranscodingDestination" />
            </bean>
        </constructor-arg>
    </bean>

    <bean id="prototypeListenerContainer"
          class="org.springframework.jms.listener.DefaultMessageListenerContainer"
          abstract="true"
          lazy-init="true">
        <property name="concurrentConsumers" value="5" />
        <property name="connectionFactory" ref="connectionFactory" />
        <!-- 2 is CLIENT_ACKNOWLEDGE: http://java.sun.com/j2ee/1.4/docs/api/constant-values.html#javax.jms.Session.CLIENT_ACKNOWLEDGE -->
        <!-- 1 is autoacknowldge -->
        <property name="sessionAcknowledgeMode" value="1" />
        <property name="sessionTransacted" value="true" />
     </bean>

     <!-- Do not deploy this bean for machines other than transcoding machine -->
     <condbean:cond test="$transcoding.server">
         <bean id="voicemailMDPContainer" parent="prototypeListenerContainer" lazy-init="true">
           <property name="destination" ref="globalVoicemailTranscodingDestination" />
           <property name="messageListener" ref="voicemailMDPListener" />
         </bean>
     </condbean:cond>

     <bean id="voicemailForwardMDPContainer" parent="prototypeListenerContainer" lazy-init="true">
       <property name="destination" ref="localVoicemailTranscodingDestination" />
       <property name="messageListener" ref="voicemailForwardingMDPListener" />
     </bean>

【讨论】:

尝试删除所有的lazy-init定义,并将prototypeListenerContainer定义的autoStartup设置为false。这将阻止侦听器容器尝试在上下文启动时获取连接,但是您需要自己找到一种在侦听器容器上手动调用 start() 的方法。哦,欢迎来到 JMS 的可怕之处。 如果我可以让 JNDI 查找继续在后台发生,让我的客户在没有服务器的情况下继续他们的快乐方式,一切都会很顺利。我会试试你的建议。谢谢。

以上是关于远程机器未运行时如何为远程 JMS 队列初始化 ConnectionFactory?的主要内容,如果未能解决你的问题,请参考以下文章

远程 JMS 队列的客户端

尝试远程连接到 Websphere 上的 JMS 队列时出现 sun/io/MalformedInputException

编写一个shell脚本ssh到远程机器并执行命令

如何将 JMS 消息从 WildFly 10 发送到远程 ActiveMQ

Spring整合Weblogic jms实战

使用 AIDL 编写 Android 服务时如何为客户端分发自定义类