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

标签 java spring jms activemq spring-jms

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

我当前的 spring 配置在代理网络(用于集群)和 DefaultMessageListenerContainers (DMLCs) 中使用嵌入式 activemq,并带有一个通用的 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>

最佳答案

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

我看到有两种方法可以提高你的表现:

  • 手动阅读消息,例如使用 JmsTemplate.receive() 方便的方法。请记住使用非常低的超时时间。通过在更少的线程(10?100?)中迭代检查每个主题,您会遇到一些延迟,但更少的线程意味着更少的上下文切换时间浪费。

    老实说,我认为这个解决方案不会扩展并为您提供更好的性能,但请尝试检查不同数量的线程和超时。
  • 簇!恐怕用一个应用程序听 20-30k 主题永远不会奏效(足够快)。但是,您似乎可以轻松扩展此任务。投入十个或一百个服务器,让每个服务器相应地监听两千或两百个主题。这将线性扩展。
  • 关于java - 数千个主题的高性能 DefaultMessageListenerContainer 配置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10948943/

    相关文章:

    java - 编译时多态性?

    Java Util Logging 重定向到 Log4j2 不适用于 Spring Boot 应用程序

    java - 何时在 Spring Boot 中使用自定义上下文路径而不是根上下文

    java - Spring Boot 多模块 考虑在配置中定义一个 type 的 bean

    javascript - 如何使用 javascript 将消息发送到 JMS 队列?

    java - 如何通过代码设置android view的layout_alignParentEnd属性?

    java.sql.SQLException : Parameter index out of range (1 > number of parameters, 即 0)。我收到这个错误

    java - 如何在 web.xml 中配置 spring-boot servlet?

    java - 如何在 WildFly 10 中 @Inject JMSContext?

    java - JmsTemplate中的异常处理发送