Camel JMS Transacted无法正常工作

时间:2018-01-18 12:41:14

标签: java apache-camel jms activemq

我正在尝试获取Camel路由JMS-> HTTP4并且事务正常工作但是当异常时我不能将消息传输到ActiveMQ.DLQ。

下面的示例说明了如果REST服务的服务器关闭并且无法传递路由,可能会发生什么。

我得到了正确的例外:

2018-01-18 12:30:50:962-[Camel (LRM-Relay) thread #5 - JmsConsumer[myIncomingQueue]] WARN  o.a.c.s.s.TransactionErrorHandler - Transaction rollback (0x30a1c779) redelivered(false) for (MessageId: ID:MGR-MacBook-Pro.local-51837-1516262355358-4:2:1:1:16 on ExchangeId: ID-MGR-MacBook-Pro-local-1516275047663-0-1) caught: java.net.ConnectException: Cannot connect to CORE REST  

2018-01-18 12:30:50:965-[Camel (LRM-Relay) thread #5 - JmsConsumer[myIncomingQueue]] WARN  o.a.c.c.j.EndpointMessageListener - Execution of JMS message listener failed. Caused by: [org.apache.camel.RuntimeCamelException - java.net.ConnectException: Cannot connect to CORE REST] 
org.apache.camel.RuntimeCamelException: java.net.ConnectException: Cannot connect to CORE REST …

但消息被消耗并从队列中删除。我的假设是使用事务/事务处理Camel和AMQ将解决此问题并将消息移至ActiveMQ.DLQ。

我已经阅读了Camel in Action 1st Ed的第9章。谷歌搜索,但没有找到任何问题的解决方案。

我知道我可以创建/定义我自己的TransactionErrorHandler()并将消息存储在我选择的队列中,但我觉得这在使用交易时是默认的...

我正在使用独立的ActiveMQ 5.15.2 vanilla安装和配置 骆驼2.20.1
MacOS 10.13.2上的Java 8_144

我的配置:

@Configuration
 public class Config {
     /**
     * The Camel context.
     */
     final CamelContext camelContext;


     /**
     * The Broker url.
     */
     @Value("${jms.broker.url}")
    private String brokerURL;


    /**
     * Instantiates a new Config.
     *
     * @param camelContext   the sisyfos context
     * @param metricRegistry the metric registry
     */
    @Autowired
    public Config(final CamelContext camelContext, final MetricRegistry metricRegistry) {
        this.camelContext = camelContext;
        this.metricRegistry = metricRegistry;
    }

    @Bean
    public ActiveMQConnectionFactory activeMQConnectionFactory() {
        final ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory();
        activeMQConnectionFactory.setBrokerURL(brokerURL);
        return activeMQConnectionFactory;
    }

    /**
     * Pooled connection factory pooled connection factory.
     *
     * @return the pooled connection factory
     */
    @Bean
    @Primary
    public PooledConnectionFactory pooledConnectionFactory() {
        final PooledConnectionFactory pooledConnectionFactory = new PooledConnectionFactory();
        pooledConnectionFactory.setMaxConnections(8);
        pooledConnectionFactory.setMaximumActiveSessionPerConnection(500);
        pooledConnectionFactory.setConnectionFactory(activeMQConnectionFactory());
        return pooledConnectionFactory;
    }

    /**
     * Jms configuration jms configuration.
     *
     * @return the jms configuration
     */
    @Bean
    public JmsConfiguration jmsConfiguration() {
        final JmsConfiguration jmsConfiguration = new JmsConfiguration();
        jmsConfiguration.setConnectionFactory(pooledConnectionFactory());
        jmsConfiguration.setTransacted(true);
        jmsConfiguration.setTransactionManager(transactionManager());
        jmsConfiguration.setConcurrentConsumers(10);

        return jmsConfiguration;
    }

    /**
     * Transaction manager jms transaction manager.
     *
     * @return the jms transaction manager
     */
    @Bean
    public JmsTransactionManager transactionManager() {
        final JmsTransactionManager transactionManager = new JmsTransactionManager();
        transactionManager.setConnectionFactory(pooledConnectionFactory());
        return transactionManager;
    }

    /**
     * Active mq component active mq component.
     *
     * @return the active mq component
     */
        @Bean
public ActiveMQComponent activeMQComponent(JmsConfiguration jmsConfiguration,
                                           PooledConnectionFactory pooledConnectionFactory,
                                           JmsTransactionManager transactionManager) {
    final ActiveMQComponent activeMQComponent = new ActiveMQComponent();
    activeMQComponent.setConfiguration(jmsConfiguration);
    activeMQComponent.setTransacted(true);
    activeMQComponent.setUsePooledConnection(true);
    activeMQComponent.setConnectionFactory(pooledConnectionFactory);
    activeMQComponent.setTransactionManager(transactionManager);
    return activeMQComponent;
}


}

我的路线:

    @Component
public class SendToCore extends SpringRouteBuilder {

    @Override
    public void configure() throws Exception {
        Logger.getLogger(SendToCore.class).info("Sending to CORE");


        //No retries if first fails due to connection error
        interceptSendToEndpoint("http4:*")
                .choice()
                .when(header("JMSRedelivered").isEqualTo("false"))
                .throwException(new ConnectException("Cannot connect to CORE REST"))
                .end();

        from("activemq:queue:myIncomingQueue")
                .transacted()
                .setHeader(Exchange.CONTENT_TYPE, constant("application/xml"))
                .to("http4:localhost/myRESTservice")
                .log("${header.CamelHttpResponseCode}")
                .end();
    }
}

您可能会在某些bean中找到冗余声明,这就是我尝试解决问题...

添加一个链接到我的Github仓库,其中有一个小型测试项目,说明了这一点:
https://github.com/hakuseki/transacted

2 个答案:

答案 0 :(得分:1)

这可能是SpringBoot自动配置的问题。

如果消息丢失而不是转到DLQ,Camel的ActiveMQ组件会自动提交它们,而不是等到工作完成。

更新:使您的示例使用Java Config

的步骤
  

注意:我的配置没有事务管理器,因为您的案例不需要它。而只需在ActiveMQComponent transactedtruelazyCreateTransactionManagerfalse中进行设置。然后你得到了一个"本地"与您的经纪人交易,这就是您所需要的一切。

  • 我从您的路线中移除了.transacted()(需要一个事务管理器,但不需要有#34; JMS本地交易"路线)
  • 我在路由类中注释掉了你的错误处理程序(需要一个事务管理器,你可以使用默认的错误处理程序)
  • MainApplication中禁用JMS和ActiveMQ的自动配置:@SpringBootApplication(exclude = { JmsAutoConfiguration.class, ActiveMQAutoConfiguration.class})
  • 用以下内容替换您的Java配置(改编自此问题:ConnectionFactory get destroyed before camel

Java配置:

@Value("${jms.broker.url}") 
String brokerURL;

@Bean
public ActiveMQConnectionFactory connectionFactory() {
    final ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory();
    activeMQConnectionFactory.setBrokerURL(brokerURL);
    return activeMQConnectionFactory;
}

@Bean
@Primary
public PooledConnectionFactory pooledConnectionFactory(ConnectionFactory cf) {
    final PooledConnectionFactory pooledConnectionFactory = new PooledConnectionFactory();
    pooledConnectionFactory.setMaxConnections(1);
    pooledConnectionFactory.setConnectionFactory(cf);
    return pooledConnectionFactory;
}

@Bean(name = "activemq")
@ConditionalOnClass(ActiveMQComponent.class)
public ActiveMQComponent activeMQComponent(ConnectionFactory connectionFactory) {
    ActiveMQComponent activeMQComponent = new ActiveMQComponent();
    activeMQComponent.setConnectionFactory(connectionFactory);
    activeMQComponent.setTransacted(true);
    activeMQComponent.setLazyCreateTransactionManager(false);
    return activeMQComponent;
}

最后,只是为了#34;运行"路线,我添加了一个小骆驼路线测试

@RunWith(CamelSpringBootRunner.class)
@SpringBootTest(classes = MainApplication.class)
public class SampleCamelApplicationTest {

    @Produce(uri = "activemq:queue:myIncomingQueue")
    protected ProducerTemplate template;

    @Test
    public void shouldProduceMessages() throws Exception {
        template.sendBody("test");
        Thread.sleep(20000); //wait for ActiveMQ redeliveries
    }

}

如果我运行此测试,则消息将转到ActiveMQ.DLQ

希望这有帮助

答案 1 :(得分:0)

注意到如果你想让Spring Boot处理那些池和配置的生命周期,你就不应该直接调用它们的方法,而是让它们作为方法签名中的参数提供

例如这个

public ActiveMQComponent activeMQComponent() {

应该是

 public ActiveMQComponent activeMQComponent(JmsConfiguration config, ConnectionFactory cf, ...) {

然后Spring Boot将为您提供这些bean。

关于您的交易无效的原因,您可以查看Camel in Action第2版书中的一些交易示例:https://github.com/camelinaction/camelinaction2/tree/master/chapter12