в предыдущей статьеПодводные камни интеграции JMS с Spring/Spring BootЯ упомянул некоторые ловушки производительности при использовании компонентов Spring JMS.В этой статье объясняются различные компоненты Spring JMS, их функции и правильный способ их использования.
JmsTemplate
Используется для отправки и получения сообщений (получение синхронно и будет заблокировано). каждыйJmsTemplate
Экземпляры имеют свою собственную конфигурацию, такую как:connectionFactory
,sessionTransacted
,sessionAcknowledgeMode
,deliveryMode
,timeToLive
и так далее, поэтому необходимо предоставлять разные конфигурации в соответствии с разными сценариями.JmsTemplate
Bean вместо Singleton Bean берет на себя все операции JMS.
Ниже приведена диаграмма классов (включены только некоторые ключевые свойства):
ConnectionFactory
Весна дает дваjavax.jms.ConnectionFactory
Реализация:SingleConnectionFactoryа такжеCachingConnectionFactory. На самом деле они являются разновидностью Wrapper, используемой для кэширования таких вещей, как:Connection
,Session
,MessageProducer
,MessageConsumer
.
ФактическиJavadoc для JmsTemplateупомянул:
NOTE: The ConnectionFactory used with this template should return pooled Connections (or a single shared Connection) as well as pooled Sessions and MessageProducers. Otherwise, performance of ad-hoc JMS operations is going to suffer.
в документации Spring JMSCaching Messaging ResourcesВ нем также упоминается необходимость оптимизации использования ресурсов для повышения производительности:
The standard API involves creating many intermediate objects. To send a message the following 'API' walk is performed
ConnectionFactory->Connection->Session->MessageProducer->send
Between the ConnectionFactory and the Send operation there are three intermediate objects that are created and destroyed. To optimise the resource usage and increase performance two implementations ofConnectionFactory
are provided.
Ниже приведена диаграмма классов (включены только некоторые ключевые свойства):
SingleConnectionFactory
SingleConnectionFactoryКак следует из названия, независимо от того, сколько раз вы звонитеcreateConnection(..)
оба возвращают одно и то жеConnection
пример. но не кешируетSession
, то есть вызов один разcreateSession(...)
Будет создан новый экземпляр.
в состоянии пройтиSingleConnectionFactoryTestУзнать больше.
Поэтому в большинстве случаев не рекомендуется использоватьSingleConnectionFactory
.
CachingConnectionFactory
CachingConnectionFactoryунаследовано отSingleConnectionFactory
, за исключением того, что кеш остается прежнимConnection
Помимо характеристик экземпляра, он также добавляетSession
,MessageProducer
,MessageConsumer
кеш.
CachingConnectionFactory
Он поддерживаетAcknowledge Mode -> List<Session>
Карта, sessionCacheSize фактически относится кList<Session>
размер, поэтому будет максимум 4 * sessionCacheSizeSession
Кэшируется (поскольку JMS определяет четыре режима подтверждения).
а такжеCachingConnectionFactory
Его сущность не является Объектом
Пул, поэтому он не будет блокироваться или возвращать значение null, поскольку фактическое количество запросов сеанса превышает sessionCacheSize, вы можете использовать его с уверенностью.
CachingConnectionFactory
каждый вернулсяSession
иметь внутриConsumerCacheKey -> MessageConsumer
так же какDestinationCacheKey -> MessageProducer
Карта, используемая для кэшированияMessageProducer
а такжеMessageConsumer
.
в состоянии пройтиCachingConnectionFactoryУзнать больше.
MessageListenerContainer
В Spring JMS есть функцияMessageListenerContainer, согласно официальной документации:
A message listener container is used to receive messages from a JMS message queue and drive the MessageListener that is injected into it.
упомянутый вышеMessageListener
то естьjavax.jms.MessageListener
, в первый раз, когда я увидел эту штуку, я почувствовал себя немного странно, потому чтоMessageListener
Формальное использование должно бытьMessageConsumer.setMessageListener()
Вот и все.
потому чтоMessageListenerContainer
унаследовано отSmartLifeCycle
, поэтому он обеспечивает функцию открытия соединения и сеанса при запуске программы и закрытия сеанса и соединения при закрытии программы, так что вам не нужно беспокоиться о восстановлении ресурсов.
Две реализации описаны нижеSimpleMessageListenerContainerа такжеDefaultMessageListenerContainer.
Ниже приведена диаграмма классов (включены только некоторые ключевые свойства):
SimpleMessageListenerContainer
SimpleMessageListenerContainerиспользоватьMessageConsumer.setMessageListener()
Для прослушивания сообщений он не поддерживает участие во внешних транзакциях (таких как PlatformTransactionManager).
Можно держать несколькоMessageConsumer
пример. код показывает, как показано ниже:
// ...
private int concurrentConsumers = 1;
private Set<Session> sessions;
private Set<MessageConsumer> consumers;
// ...
protected void initializeConsumers() throws JMSException {
// Register Sessions and MessageConsumers.
synchronized (this.consumersMonitor) {
if (this.consumers == null) {
this.sessions = new HashSet<Session>(this.concurrentConsumers);
this.consumers = new HashSet<MessageConsumer>(this.concurrentConsumers);
Connection con = getSharedConnection();
for (int i = 0; i < this.concurrentConsumers; i++) {
Session session = createSession(con);
MessageConsumer consumer = createListenerConsumer(session);
this.sessions.add(session);
this.consumers.add(consumer);
}
}
}
}
Существует два способа обработки сообщений: 1) традиционныйMessageConsumer.setMessageListener()
;2) использоватьExecutor
.
if (this.taskExecutor != null) {
consumer.setMessageListener(new MessageListener() {
@Override
public void onMessage(final Message message) {
taskExecutor.execute(new Runnable() {
@Override
public void run() {
processMessage(message, session);
}
});
}
});
}
else {
consumer.setMessageListener(new MessageListener() {
@Override
public void onMessage(Message message) {
processMessage(message, session);
}
});
}
DefaultMessageListenerContainer
DefaultMessageListenerContainerа такжеSimpleMessageListenerContainerРазличный, он используетMessageConsumer.receive()
для обработки сообщений и поддержки XA
сделка.
потому чтоreceive()
это синхронный, блокирующий метод, производительность которого неsetMessageListener()
Хорошо, так что это очень зависит от многопоточности (TaskExecutor
), что также дает преимущества динамического масштабирования.
Пожалуйста, будьте осторожны, чтобы не использовать многопоточность для тем, иначе вы будете получать дублирующиеся сообщения.официальная документация.
Получать сообщения асинхронно
Способы получения сообщений синхронно:JmsTemplate.receive*()
а такжеMessageConsumer.receive*()
, я не буду здесь много говорить, а сосредоточусь на нескольких способах асинхронного получения сообщений.
MessageListener & MessageListenerContainer
ПучокMessageListener
упакован вMessageListenerContainer
Получать сообщения, примеры смотрите в официальной документацииAsynchronous Reception - Message-Driven POJOs
SessionAwareMessageListener
SessionAwareMessageListenerпредоставляется Spring иMessageListener
похожий интерфейс,MessageListenerContainer
Этот интерфейс поддерживается, использование иMessageListener
Такой же.
MessageListenerAdapter
MessageListenerAdapter— это еще один способ асинхронного получения сообщений, предоставляемый Spring.MessageListener
а такжеSessionAwareMessageListener
Более гибкий, потому что он использует отражение для передачи сообщений вашим методам, которые получают сообщения.
См. официальную документацию по использованиюthe SessionAwareMessageListener interface.
@JmsListener
@JmsListenerэто еще один способ получения сообщений, как им пользоваться можно посмотреть в официальной документацииAnnotation-driven listener endpoints.
@JmsListener
а такжеMessageListener
,SessionAwareMessageListener
,MessageListenerAdapter
Например, также нужен контейнер, пользователи могут@JmsListener.containerFactory
атрибут для указанияJmsListenerContainerFactory.
Spring предоставляет две реализации JmsListenerContainerFactory:
- DefaultJmsListenerContainerFactory, используемый для создания DefaultMessageListenerContainer, предоставляемого Spring Boot
DefaultJmsListenerContainerFactoryConfigurer
как инструмент настройки - SimpleJmsListenerContainerFactory, используемый для создания SimpleMessageListenerContainer
Итак, используя@JmsListener
Тщательный выбор правильногоJmsListenerContainerFactory
, а не принимать одну конфигурацию глобально.
Суммировать
При использовании Spring JMS следует помнить о трех вещах:
- В соответствии с реальной ситуацией настройте соответствующий компонент ConnectionFactory, при необходимости может быть несколько компонентов ConnectionFactory.
- JmsTemplate, MessageListenerContainer, JmsListenerContainerFactory необходимо настроить разные bean-компоненты в соответствии с реальной ситуацией, чтобы избежать глобального использования одного набора.
- JmsTemplate, MessageListenerContainer, JmsListenerContainerFactory выбирают соответствующий ConnectionFactory.
- Установите соответствующий размер пула исполнителей/потоков, чтобы избежать большого количества блоков потоков.
Ниже приведена диаграмма взаимосвязи между различными компонентами.