数千个主题的高性能DefaultMessageListenerContainer配置

时间:2012-06-08 12:35:06

标签: java spring jms activemq spring-jms

对于我当前的项目,我需要使用来自多个目的地的消息(从数百到20或30k)所有目的地都是主题。当前(用于初始加载测试)所有消息都是在同一服务器上的线程池中创建的。

我当前的spring配置使用嵌入式activemq在代理网络(用于群集)和DefaultMessageListenerContainers(DMLCs)中使用公共TaskExecutor。虽然目的地数量非常多,但每个目的地的吞吐量相对较低。

我唯一的要求就是尽快消费所有消息。

我的配置:

<bean id="connectionfactory" class="org.springframework.jms.connection.CachingConnectionFactory" destroy-method="destroy">
    <property name="targetConnectionFactory">
        <ref bean="amqConnectionFactory" />
    </property>
</bean>
<bean id="amqConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
    <property name="brokerURL" value="vm://localhost:61616?async=false&amp;jms.dispatchAsync=false" />
    <property name="userName" value="admin" />
    <property name="password" value="admin" />
</bean>
<bean id="listenerThreadPoolTaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
    <property name="corePoolSize" value="70" />
    <property name="maxPoolSize" value="70" />
    <property name="daemon" value="true" />
    <property name="keepAliveSeconds" value="60" />
</bean>

<!-- Message Listener Container Template for Topics -->
<bean id="topiccontainertemplate" class="org.springframework.jms.listener.DefaultMessageListenerContainer" scope="prototype"
    destroy-method="destroy">
    <property name="autoStartup" value="false" />
    <property name="connectionFactory" ref="connectionfactory" />
    <property name="pubSubDomain" value="true" />
    <property name="cacheLevelName" value="CACHE_CONSUMER" />
    <property name="destinationName" value="default" />
    <property name="maxMessagesPerTask" value="1" />
    <property name="receiveTimeout" value="1" />
    <property name="taskExecutor" ref="listenerThreadPoolTaskExecutor" />
</bean>

我的代码使用应用程序上下文作为DMLC-Factory并设置容器的最终配置:

    AbstractMessageListenerContainer container = context.getBean("simpletopiccontainertemplate", AbstractMessageListenerContainer.class);
    container.setDestinationName(localEntity.getId().getDestination());
    container.setMessageListener(mylistener);
    container.start();

虽然我们不会在此配置中丢失消息,但单个消息的转换时间可能非常长。

Q1:是否有更有效的方式来收听大量目的地?

Q2:我的侦听器配置是否有可能改进?

Q3:除了DMLC,我还尝试了SimpleMessageListenerContainer,但我无法让它工作。我的配置有问题吗?

<bean id="simpletopiccontainertemplate" class="org.springframework.jms.listener.SimpleMessageListenerContainer" scope="prototype"
    destroy-method="destroy">
    <property name="autoStartup" value="false" />
    <property name="connectionFactory" ref="connectionfactory" />
    <property name="destinationName" value="default" />
    <property name="concurrency" value="1" />
    <property name="pubSubDomain" value="true" />
    <property name="taskExecutor" ref="listenerThreadPoolTaskExecutor" />
</bean>

2 个答案:

答案 0 :(得分:4)

我是否正确理解您是否手动创建了20-30万个SimpleMessageListenerContainer,每个都在聆听不同的主题?我很惊讶它甚至可以工作,因为默认情况下每个监听器都创建一个线程,并且在一个JVM中同时运行的20-30万个线程令人印象深刻(并且可怕)。

我认为有两种方法可以改善您的表现:

  • 手动阅读邮件,例如使用JmsTemplate.receive()便捷方法。只记得使用非常低的超时。通过以较少的线程(10?100?)迭代检查每个主题,您将遇到一些延迟,但线程越少意味着上下文切换浪费的时间就越少。

    老实说,我不认为这个解决方案会扩展并为您提供更好的性能,但请尝试检查不同数量的线程和超时。

  • 群集!我担心用一个应用程序监听20-30k主题将永远不会工作(足够快)。但是,您似乎可以轻松地扩展此任务。扔掉十个或者几百个服务器,让每个服务器相应地听两千或二百个主题。这将按比例线性扩展。

答案 1 :(得分:0)

我离开了DefaultMessageListenerContainerSimpleMessageListenerContainer。简单容器使用推送消息传递。这种方式(通过适当的配置,嵌入式代理将消息推送到监听器而不是轮询目的地。