簡體   English   中英

2個JMS代理(ActiveMQ)之間的XA事務

[英]XA Transactions between 2 JMS Brokers (ActiveMQ)

我正在嘗試在2個不同的遠程activeMQ代理之間移動jms消息,並且經過大量閱讀

我正在使用Atomikos,因為我正在編寫一個獨立的應用程序,同時我還在使用spring使整個事情正常進行。

我有以下bean javaconfig設置

@Bean(name="atomikosSrcConnectionFactory")
    public AtomikosConnectionFactoryBean consumerXAConnectionFactory() {
        AtomikosConnectionFactoryBean consumerBean = new AtomikosConnectionFactoryBean();
        consumerBean.setUniqueResourceName("atomikosSrcConnectionFactory");
        consumerBean.setLocalTransactionMode(false);
        return consumerBean;
    }

    @Bean(name="atomikosDstConnectionFactory")
    public AtomikosConnectionFactoryBean producerXAConnectionFactory() {
        AtomikosConnectionFactoryBean producerBean = new AtomikosConnectionFactoryBean();
        producerBean.setUniqueResourceName("atomikosDstConnectionFactory");
        producerBean.setLocalTransactionMode(false);
        return producerBean;
    }

    @Bean(name="jtaTransactionManager")
    public JtaTransactionManager jtaTransactionManager() throws SystemException {
        JtaTransactionManager jtaTM = new JtaTransactionManager();
        jtaTM.setTransactionManager(userTransactionManager());
        jtaTM.setUserTransaction(userTransactionImp());
        return jtaTM;
    }

    @Bean(initMethod="init", destroyMethod="close", name="userTransactionManager")
    public UserTransactionManager userTransactionManager() {
        UserTransactionManager utm = new UserTransactionManager();
        utm.setForceShutdown(false);
        return utm;
    }

    @Bean(name="userTransactionImp")
    public UserTransactionImp userTransactionImp() throws SystemException {
        UserTransactionImp uti = new UserTransactionImp();
        uti.setTransactionTimeout(300);
        return uti;
    }

    @Bean(name="jmsContainer")
    @Lazy(value=true)
    public DefaultMessageListenerContainer jmsContainer() throws SystemException {
        DefaultMessageListenerContainer dmlc = new DefaultMessageListenerContainer();
        dmlc.setAutoStartup(false);
        dmlc.setTransactionManager(jtaTransactionManager());
        dmlc.setSessionTransacted(true);
        dmlc.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE);
        dmlc.setConnectionFactory(consumerXAConnectionFactory());
        dmlc.setDestinationName("srcQueue");
        return dmlc;
    }

    @Bean(name="transactedJmsTemplate")
    public JmsTemplate transactedJmsTemplate() {

        DynamicDestinationResolver dest = new DynamicDestinationResolver();

        JmsTemplate jmsTmp = new JmsTemplate(producerXAConnectionFactory());

        jmsTmp.setDeliveryPersistent(true);
        jmsTmp.setSessionTransacted(true);
        jmsTmp.setDestinationResolver(dest);
        jmsTmp.setPubSubDomain(false);
        jmsTmp.setReceiveTimeout(20000);
        jmsTmp.setExplicitQosEnabled(true);
        jmsTmp.setSessionTransacted(true);
        jmsTmp.setDefaultDestination(new ActiveMQQueue("destQueue"));
        jmsTmp.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE);

        return jmsTmp;
    }

在啟動DMLC之前,2個AtomikosConnectionFactoryBean在運行時包裝ActiveMQXAConnectionFactory(每個代理一個)。

然后,我使用以下方法設置一個簡單的messageListener(在啟動之前已分配給dmlc):

public void onMessage(Message message) {
    final Message rcvedMsg = message;

    try{
        MessageCreator msgCreator = new MessageCreator(){
                public Message createMessage(Session session) throws JMSException{
                    Message returnMsg = null;
                    if(rcvedMsg instanceof TextMessage){
                        TextMessage txtMsg = session.createTextMessage();
                        txtMsg.setText(((TextMessage) rcvedMsg).getText());
                        returnMsg = txtMsg;
                    }
                    else if(rcvedMsg instanceof BytesMessage){
                        BytesMessage bytesMsg = session.createBytesMessage();
                        if(!(((BytesMessage) rcvedMsg).getBodyLength() > Integer.MAX_VALUE)){
                            byte[] bodyContent = new byte[(int) ((BytesMessage) rcvedMsg).getBodyLength()];
                            bytesMsg.writeBytes(bodyContent);
                            returnMsg = bytesMsg;
                        }
                    }
                    return returnMsg;
                }
            };

            jmsTemplate.send(msgCreator);
    }
    catch(JmsException | JMSException e){
        logger.error("Error when transfering message: '{}'. {}",message,e);
    }
}

該應用程序啟動時沒有任何特定的錯誤或警告,但是,一旦我在源隊列中放入一條消息,我就可以通過日志看到onMessage方法針對同一條消息反復運行,就像事務一直在回滾並再次重新啟動(任何地方都不會拋出錯誤)。

我還注意到,如果我碰巧使用相同的源和目標url(意味着相同的代理,但每個代理都有其自己的connectionFactory),它將起作用,並且消息將按預期在源隊列和目標隊列之間進行傳輸。

我想知道的是

  1. 我在設置中做錯了什么? 為什么當使用2個不同的代理時,我的事務“似乎”被一遍又一遍地回滾,但是當使用相同的代理(但是在2個不同的連接工廠中)時卻可以工作?
  2. 我並不完全相信onMessage當前正在執行正確的事務,因為我當前正在捕獲所有異常並且什么也不做,並且我相信這將在jmstemplate完成發送消息之前提交dmlc的事務,但是我不確定。 如果是這樣,那么SessionAwareMessageListener會更好嗎? 我應該在onMessage方法中設置@Transacted嗎?

有人可以幫助解決這個問題嗎? 歡迎所有輸入。

更新:

我意識到“回滾”的問題是由於我使用的兩個AMQ通過代理網絡相互連接,而我恰巧對源和目標使用了相同的隊列名稱。 這導致以下事實:應用程序將消息從一個AMQ傳輸到另一個AMQ,然后立即將其傳輸,因為源AMQ上有一個使用者,因此該消息將被傳輸回原始AMQ,這又被視為原始AMQ。我的應用程序發送了新消息並再次傳輸,循環無限進行。 下面發布的解決方案有助於解決其他問題。

try {
   ... Code
} catch (JmsException je) {
    logger.error("Error when transfering message: '{}'. {}",message,e);
}

上面的代碼吞沒了異常,您不應捕獲異常或將其重新拋出,以便事務管理可以適當地處理它。 當前沒有異常,執行提交會導致奇怪的結果。

我將執行以下操作, JmsException來自Spring,並且作為Spring中的大多數異常,它來自RuntimeException 只需rehtrow,還要記錄異常stacktrace,以正確除去日志語句中的第二個{}

try {
   ... Code
} catch (JmsException je) {
    logger.error("Error when transfering message: '{}'.",message,e);
    throw je;
}

但是,這將復制日志記錄,因為Spring還將記錄錯誤。

對於JMSException請執行以下操作,將其轉換為JmsException

try {
   ... Code
} catch (JMSException je) {
    logger.error("Error when transfering message: '{}'.",message,e);
    throw JmsUtils.convertJmsAccessException(je);
}

要獲取有關發生的情況的更多信息,您可能需要為org.springframework.jms包啟用DEBUG日志記錄。 這將使您深入了解發送/接收消息時會發生什么。

另一件事是您使用事務會話和手動確認消息,但是您沒有在代碼中執行message.acknowledge() 由於JTA事務,Spring不會調用它。 嘗試將其切換為SESSION_TRANSACTED 至少對於DMLC

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM