[ 
https://issues.apache.org/jira/browse/CAMEL-26658?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Work on CAMEL-26658 started by Federico Mariani.
------------------------------------------------
> camel-amqp - the producer opens a new broker connection per message when the 
> component builds the ConnectionFactory
> -------------------------------------------------------------------------------------------------------------------
>
>                 Key: CAMEL-26658
>                 URL: https://issues.apache.org/jira/browse/CAMEL-26658
>             Project: Camel
>          Issue Type: Improvement
>          Components: camel-amqp
>    Affects Versions: 4.18.5, 4.22.2, 4.23.0
>            Reporter: Federico Mariani
>            Assignee: Federico Mariani
>            Priority: Major
>
> h3. Problem
> When {{camel-amqp}} builds the {{ConnectionFactory}} itself, from 
> {{brokerUrl}} or from {{host}}/{{port}}/{{username}}/{{password}}/{{useSsl}}, 
> the producer opens a new AMQP connection for every message and closes it 
> again. On a real network that is TCP, TLS, SASL and AMQP open/begin/attach 
> per send.
> * 
> {{components/camel-amqp/src/main/java/org/apache/camel/component/amqp/AMQPComponent.java}}
>  lines 107-162 ({{doInit}}) set a plain qpid-jms {{JmsConnectionFactory}} on 
> the configuration. The static factories {{AMQPComponent.amqpComponent(...)}} 
> (lines 92-102) do the same.
> * {{camel-jms}} does not wrap it:
> ** {{JmsConfiguration.createInOnlyTemplate}} (line 780; also used by the 
> InOut template) takes {{getOrCreateTemplateConnectionFactory()}}.
> ** That falls back to the same factory ({{createTemplateConnectionFactory}}, 
> lines 1875-1877).
> * {{JmsProducer.doSend}} (lines 453-463) calls {{CamelJmsTemplate.send}}, 
> which calls Spring {{JmsTemplate.execute(callback, false)}}. With no 
> transactional session bound, it calls {{createConnection()}} and closes the 
> connection after each call (spring-framework 7.0.10 {{JmsTemplate.java}} 
> lines 520-548; the class javadoc asks for a caching or pooling factory).
> * {{cacheLevel}} only applies to the listener containers, not to the template.
> * {{camel-activemq}} wraps the factory it builds in a pool by default 
> ({{usePooledConnection=true}}), and the {{camel-jms}} docs explain pooling. 
> The {{camel-amqp}} docs never mention it, and the YAML/properties example at 
> {{amqp-component.adoc}} line 112 binds a plain {{JmsConnectionFactory}}.
> h3. How to reproduce
> Measured on main 7978db3d42dd, with a test in {{camel-amqp}}:
> * Embedded Artemis 2.57.0 with the AMQP acceptor 
> ({{ArtemisServiceFactory.createAMQPService()}}).
> * Route {{from("direct:send").to("amqp:queue:verify")}}: 50 warm-up sends, 
> then 30 measured.
> * Broker connections are counted with 
> {{ActiveMQServer.getTotalConnectionCount()}}.
> * Loopback, no TLS or SASL.
> ||Component setup||Factory used||New broker connections for 30 sends||Median 
> per send||
> |{{setHost("localhost")}}, {{setPort(port)}}|{{JmsConnectionFactory}}|30|5.4 
> ms|
> |{{setBrokerUrl("amqp://localhost:port")}}|{{JmsConnectionFactory}}|30|4.6 ms|
> |control: {{setConnectionFactory(new 
> CachingConnectionFactory(plain))}}|{{CachingConnectionFactory}}|0|0.55 ms|
> A counting proxy around the factory the component built recorded 30 
> {{createConnection()}} calls for 30 sends.
> h3. Proposed change
> * Wrap only the template connection factory that the component builds 
> ({{setTemplateConnectionFactory}}) in Spring's {{CachingConnectionFactory}}.
> ** It is already on the classpath through {{spring-jms}}, so there is no new 
> dependency. pooled-jms, which {{camel-activemq}} uses, is not a 
> {{camel-amqp}} dependency.
> ** Put it behind a new component option (for example {{cacheConnection}}) 
> that defaults to {{true}}.
> ** Apply it also to the static {{amqpComponent(...)}} factories.
> ** Leave a factory supplied by the user untouched.
> * Destroy the caching factory (close its shared connection) when the 
> component stops.
> * Keep the listener containers on the plain factory. The Spring 
> {{DefaultMessageListenerContainer}} javadoc lists the limitations of a 
> {{CachingConnectionFactory}} under a listener container.
> * *Transactions (source reading, not tested):*
> ** The lazily created {{JmsTransactionManager}} uses 
> {{getOrCreateConnectionFactory()}} ({{JmsConfiguration}} line 1884).
> ** Spring looks up the transactional session by factory instance 
> ({{ConnectionFactoryUtils}} line 286).
> ** So a producer with a wrapped template factory would no longer join that 
> transaction. Skip the wrapping when {{transacted=true}} or a 
> {{transactionManager}} is set, or build the transaction manager on the same 
> factory.
> * {{CachingConnectionFactory}} caches 1 session by default 
> ({{sessionCacheSize=1}}). Concurrent producers beyond that still create a 
> session per send on the shared connection; consider a larger size or exposing 
> it.
> * *Docs:*
> ** Change the example at {{amqp-component.adoc}} line 112 to 
> {{camel.component.amqp.broker-url = amqp://localhost:5672}}.
> ** Add a short section on connection caching and pooling for factories 
> supplied by the user, as {{jms-component.adoc}} has.
> h3. Impact / upgrade
> * This changes a default: one shared connection instead of one per message, 
> for the factories the component builds. It needs an entry in 
> {{camel-4x-upgrade-guide-4_24.adoc}}, including how to switch it off.
> * Request/reply producers use the same template factory and are covered.
> * Consumers are not affected.
> * The same code is in 4.18.5, 4.22.2 and 4.23.0.
> h3. Related
> * CAMEL-22161: added the {{brokerUrl}} component option.
> * CAMEL-20521: transacted sends.
> * CAMEL-12070: a {{CachingConnectionFactory}} that was not shut down with the 
> bundle. The cache must be stopped with the component.
> _Claude Code on behalf of Federico Mariani_



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to