资讯专栏INFORMATION COLUMN

使用Spring/Spring Boot集成JMS的陷阱

xcold / 2443人阅读

摘要:本文旨在指出中集成的一些性能陷阱,在另一篇文章各组件详解里有组件介绍及如何正确使用的内容。因此的做法会大大降低性能,并且将大部分的时间都花在反复重建这些对象上。提供的可以让使用避免频繁创建的问题。至于使用的性能测试则留给同学自己做了。

Github

本文旨在指出Spring/Spring Boot中集成JMS的一些性能陷阱,在另一篇文章Spring JMS各组件详解里有Spring JMS组件介绍及如何正确使用的内容。

JmsTemplate性能问题

Spring提供了JmsTemplate用来简化JMS的操作,但是JmsTemplate的性能是有很大问题的,主要体现在以下几个方面:

频繁创建connection,session,producer

根据Artemis官方文档的说法,JmsTemplate是一种anti-pattern,如果在使用JmsTemplate的情况下觉得很慢,那么就不要怪Artemis。

并且ConnectionSessionProducerConsumer这些对象本身设计上是可以复用的。因此JmsTemplate的做法会大大降低性能,并且将大部分的时间都花在反复重建这些对象上。

Spring提供的Workaround

可以让JmsTemplate使用SingleConnectionFactory避免频繁创建Connection的问题。或者其子类CachingConnectionFactory避免频繁创建ConnectionSessionProducerConsumer的问题。

频繁创建临时Queue

JmsTemplate#sendAndReceive方法可以用来模拟RPC调用过程,内部原理是:

A程序创建一个临时Queue作为接受相应的Queue

send一个Message到Queue,并在这个临时Queue上等待结果

B程序consume了这个Message把结果send到那个临时Queue

A接受到结果,把这个临时Queue干掉

当然整个过程中还包括前面提到的创建ConnectionSessionProducerConsumer的动作。

不出所料,Artemis官方文档也提到了,频繁创建临时Queue是一种anti-pattern,会大大影响性能。

@JmsListener性能问题 使用sync方法consume消息

使用@JmsListener注解可以很方便的用来consume消息,但它是同步而非异步,这个和官方文档所说的恰恰相反(关于sync/async consume消息的资料)。

Spring Boot的JmsAnnotationDrivenConfiguration默认使用DefaultJmsListenerContainerFactory生成DefaultMessageListenerContainer ,而它的内部原理是使用TaskExecutor发起多个线程同时从Queue中拉取消息,这也就是为什么Spring官方文档里说如果监听的是Topicconcurrency > 1,那么可能会收到重复消息的原因。

DefaultMessageListenerContainer的javadoc中说道:

Actual MessageListener execution happens in asynchronous work units which are created through Spring"s TaskExecutor abstraction

这里就有个矛盾,如果使用async的方式consume消息,那么只需给consumer设置MessageListener就行了,何必使用TaskExecutor呢?

一看代码果然不出所料:

DefaultMessageListenerContainer#runcallinvokeListener

然后callAbstractPollingMessageListenerContainer#receiveAndExecute

然后calldoReceiveAndExecute

然后callreceiveMessage

然后callreceiveFromConsumer

然后callJmsDestinationAccessor#receiveFromConsumer这个方法调用了MessageConsumer#consume

也就是说Spring只是使用一个或多个线程在不停的同步的consume消息而已。

虽然可以使用concurrency参数提高并发,但是多线程从Queue/Topic中consume消息的性能比javax.jms.MessageConsumer#setMessageListener的方法要低上很多。

有兴趣的同学可以使用Artemis Example(下载地址)里的JMS Perf代码做一下测试,它的测试代码用的是javax.jms.MessageConsumer#setMessageListener的方式来consume消息的,在配置正确的情况下可以达到接近10w/s。至于使用MessageConsumer.receive的性能测试则留给同学自己做了。

为@JmsListener创建的session默认transacted=true

还是之前提到的JmsAnnotationDrivenConfiguration,使用的DefaultJmsListenerContainerFactoryConfigurer默认是把session设置为transacted的。

根据测算,当一个session是transacted的时候其性能会相差20%,有兴趣的同学可以使用Artemis Example(下载地址)里的JMS Perf代码做一下测试。

下载之后找到examples/jms/perf目录,看这个目录下的readme.html获得执行方法,在执行之前修改src/main/resources/perf.properties文件,下面是部分参数的解释:

num-messages,测试多少个消息

num-warmup-messages,热身用的消息数,热身过之后性能会提升

durable,对应DeliveryMode

transacted,是否transacted

batch-size,批量commit的大小

drain-queue,是否测试前先把队列里已有的消息都清空

可以使用以下两套配置对比transacted的性能差别:

配置一,transacted=false:

num-messages=100000
num-warmup-messages=1000
message-size=1024
durable=false
transacted=false
batch-size=1
drain-queue=true
destination-lookup=perfQueue
connection-factory-lookup=/ConnectionFactory
throttle-rate=-1
dups-ok-acknowledge=false
disable-message-id=true
disable-message-timestamp=true

配置二,transacted=true:

num-messages=100000
num-warmup-messages=1000
message-size=1024
durable=false
transacted=true
batch-size=1
drain-queue=true
destination-lookup=perfQueue
connection-factory-lookup=/ConnectionFactory
throttle-rate=-1
dups-ok-acknowledge=false
disable-message-id=true
disable-message-timestamp=true
@JmsListener创建的session默认加入了事务控制

关于加入事务控制是否会有性能问题没有实际测试过,不过值得注意的这是Spring Boot的默认行为。

相关连接:代码1, 代码2,代码3,Javadoc。

Spring Boot配置 ConnectionFactory全局只有一个实例

Spring将JMS的集成变得非常简单,只需提供几个配置参数就可以了,但是只能提供一种默认配置,不管业务场景如何都使用同一种配置是非常有问题的。

spring-boot提供了以下几种方式(文档)集成JMS:

JNDI

Artemis, native模式和embedded模式

ActiveMQ

这几种模式的缺点都是只能配置一个ConnectionFactory,这对于简单应用来说没问题,对于复杂应用来说就显得不够用了,这让你丧失了针对不同场景配置不同ConnectionFactory的机会。
而且Artemis的native模式只支持host:port,不支持更丰富参数的url模式。

@JmsListener的默认配置也就只提供一种

看过DefaultJmsListenerContainerFactoryConfigurerJmsAnnotationDrivenConfiguration的代码就明白了。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/64939.html

相关文章

  • Spring Framework 参考文档(概述)

    摘要:除此之外,还为不同的应用程序体系结构提供了基础支持,包括消息传递事务数据和持久性以及,它还包括基于的框架,以及与之并行的反应性框架。还支持依赖项注入和公共注解规范,应用程序开发人员可以选择使用这些规范,而不是提供的特定于的机制。 概述 Spring使创建Java企业应用程序变得很容易,它提供了在企业环境中使用Java语言所需要的一切,支持Groovy和Kotlin作为JVM上的替代语言...

    silencezwm 评论0 收藏0
  • SpringSpring Boot和TestNG测试指南 - 使用Spring Boot Test

    摘要:地址前面一个部分讲解了如何使用工具来测试项目,现在我们讲解如何使用工具来测试项目。所以我们可以利用这个特性来进一步简化测试代码。因为只有这样才能够在测试环境下发现生产环境的问题,也避免出现一些因为配置不同导致的奇怪问题。 Github地址 前面一个部分讲解了如何使用Spring Testing工具来测试Spring项目,现在我们讲解如何使用Spring Boot Testing工具来测...

    Anshiii 评论0 收藏0
  • 次世代会话管理项目 Spring Session

    摘要:会话管理一直是企业级应用的重要部分。传统会话管理技术的问题的目的是解决传统的会话管理技术的各种问题。对如和之类的闭源产品,找到适合它们的会话管理技术的替代实现则通常是不可能的。典型的应用会将当前用户的身份及其安全级别或角色存储在会话里面。 欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文来自云+社区翻译社,由Tnecesoc编译。 会话管理一直是 Java 企业级应用的...

    不知名网友 评论0 收藏0
  • Spring核心 Spring简介

    摘要:基于工厂,会有多种应用上下文的实现的模块在模块中,面向切面编程提供了丰富的支持,该模块是应用系统中开发切面的基础,可以帮助应用对象解耦。的主页安全对于许多应用都是一个非常关键的切面。 简化Java开发 JavaBean:Enterprise JavaBean、EJBJDO:Java数据对象、Java Data ObjectPOJO:Plain Old Java ObjectDI:依赖注...

    sixgo 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<