ConnectionFactory get destroyed before camel

2019-06-10 20:04发布

问题:

I'm using spring-boot with camel and ActiveMQ.

I'm using activemq autoconfiguration via @EnableJms annotation. But creating my own ActiveMQComponent to enable "transacted(true)" on all queues.

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

It works well but when I try to gracefully shutdown the application. The PooledConnectionFactory get destroyed before the camel graceful shutdown happens.

Leading to a tons of error and the route unable to correctly stops.

Like 20 times this error :

2017-05-04 18:21:59.748  WARN 12188 --- [er[test.queue]] o.a.activemq.jms.pool.PooledSession      : Caught exception trying rollback() when putting session back into the pool, will invalidate. javax.jms.IllegalStateException: The Session is closed

Followed by:

2017-05-04 18:21:59.748  INFO 12188 --- [      Thread-18] o.a.camel.spring.SpringCamelContext      : Apache Camel 2.18.3 (CamelContext: route) is shutting down

Then later :

2017-05-04 18:21:59.766 INFO 12188 --- [ - ShutdownTask] o.a.camel.impl.DefaultShutdownStrategy : Waiting as there are still 1 inflight and pending exchanges to complete, timeout in 300 seconds. Inflights per route: [test2 = 1]

Anyone can help me configuring spring-boot camel activemq all together with graceful shutdown ?

Thanks

Update : Here is a sample of my pom.xml:

        <properties>

        <!-- Spring -->
        <spring-boot.version>1.4.3.RELEASE</spring-boot.version>

        <!-- Camel -->
        <camel-spring-boot.version>2.18.3</camel-spring-boot.version>
    </properties>
    ....

    <!-- Camel BOM -->
        <dependency>
            <!-- Import dependency management from Spring Boot -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${spring-boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-spring-boot-dependencies</artifactId>
            <version>${camel-spring-boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
...
    <!-- Spring -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <exclusions>
            <exclusion>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-tomcat</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jetty</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-actuator</artifactId>
    </dependency>

    <!-- ActiveMQ -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-activemq</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.activemq</groupId>
        <artifactId>activemq-camel</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.activemq</groupId>
        <artifactId>activemq-pool</artifactId>
    </dependency>

    <!-- Camel -->
    <dependency>
        <groupId>org.apache.camel</groupId>
        <artifactId>camel-spring-boot-starter</artifactId>
    </dependency>

Update 2: After further investigation and the creation of a new project adding every modification one by one I have isolated the problem.

The shutdown works correcly until I add a specific endpoint :

@EndpointInject(uri = "direct:aaa")
private Endpoint errorHandling;

Using :

private String errorHandling = "direct:aaa";

Doesn't produce the bug.

It seems like using @EndpointInject is making the activemq close first

Update 3 :

Found that SpringCamelContext is not implementing ApplicationListener and thus its method "onApplicationEvent" its not called handling the "shutdownEager" of camel.

回答1:

Important thing is to use Camel Spring Boot Starter.

http://camel.apache.org/spring-boot.html

How to enable Camel auto-configuration in my Spring Boot application?

Just drop camel-spring-boot jar into your classpath:

 <dependency>
     <groupId>org.apache.camel</groupId>
     <artifactId>camel-spring-boot</artifactId>
     <version>${camel.version}</version> <!-- use the same version as your Camel core version -->
</dependency>

camel-spring-boot jar comes with the spring.factories file, so as soon as you add that dependency into your classpath, Spring Boot will automatically auto-configure the Camel for you. Yay! That was fast ;) .

Auto-configured Camel context

The most important piece of functionality provided by the Camel auto-configuration is the CamelContext instance.

Camel auto-configuration creates SpringCamelContext for your and take care of the proper initialization and shutdown of that context.

Created Camel context is also registered in the Spring application context (under camelContext bean name), so you can access it just as the any other Spring bean.

@Configuration
public class MyAppConfig {

  @Autowired
  CamelContext camelContext;

  @Bean
  MyService myService() {
    return new DefaultMyService(camelContext);
  }
}


回答2:

Apparently since https://issues.apache.org/jira/browse/CAMEL-2607 the SpringCamelContext doesn't implement ApplicationListener interface anymore.

Since I'm using spring-boot autoconfiguration, I am not using CamelContextFactoryBean which is adding the listener.

Has a temporary fix, I created a component which listen to ApplicationEvent and dispatch them to the SpringCamelContext method :

public class SpringCamelContextFix implements ApplicationListener<ApplicationEvent> {

private SpringCamelContext camelContext;

public SpringCamelContextFix(SpringCamelContext camelContext) {
    this.camelContext = camelContext;
}

@Override
public void onApplicationEvent(ApplicationEvent event) {
    camelContext.onApplicationEvent(event);
}

}



回答3:

I had this same problem running unit/integration tests with Spring Boot, ActiveMQ or A-MQ, and Camel (version 2.18.1.redhat-000012). Apparently, when Spring Boot shuts down, the JMS thread pool is closed before the Camel context is shutdown, which is the wrong order. @John D provided a code fix in a Camel users mailing list thread which is similar to what he provided in this thread. Here is the version of John D's code that worked for me:

@Component 
public class SpringCamelContextFix implements 
ApplicationListener<ApplicationEvent> {

    @Inject
    private SpringCamelContext camelContext;

    public SpringCamelContextFix(SpringCamelContext camelContext) {
        this.camelContext = camelContext;
    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        camelContext.onApplicationEvent(event);
    }
}