摘要:本文旨在指出中集成的一些性能陷阱,在另一篇文章各組件詳解里有組件介紹及如何正確使用的內(nèi)容。因此的做法會(huì)大大降低性能,并且將大部分的時(shí)間都花在反復(fù)重建這些對(duì)象上。提供的可以讓使用避免頻繁創(chuàng)建的問(wèn)題。至于使用的性能測(cè)試則留給同學(xué)自己做了。
Github
本文旨在指出Spring/Spring Boot中集成JMS的一些性能陷阱,在另一篇文章Spring JMS各組件詳解里有Spring JMS組件介紹及如何正確使用的內(nèi)容。
JmsTemplate性能問(wèn)題Spring提供了JmsTemplate用來(lái)簡(jiǎn)化JMS的操作,但是JmsTemplate的性能是有很大問(wèn)題的,主要體現(xiàn)在以下幾個(gè)方面:
頻繁創(chuàng)建connection,session,producer根據(jù)Artemis官方文檔的說(shuō)法,JmsTemplate是一種anti-pattern,如果在使用JmsTemplate的情況下覺(jué)得很慢,那么就不要怪Artemis。
并且Connection,Session,Producer,Consumer這些對(duì)象本身設(shè)計(jì)上是可以復(fù)用的。因此JmsTemplate的做法會(huì)大大降低性能,并且將大部分的時(shí)間都花在反復(fù)重建這些對(duì)象上。
Spring提供的Workaround
可以讓JmsTemplate使用SingleConnectionFactory避免頻繁創(chuàng)建Connection的問(wèn)題?;蛘咂渥宇?lèi)CachingConnectionFactory避免頻繁創(chuàng)建Connection,Session,Producer,Consumer的問(wèn)題。
頻繁創(chuàng)建臨時(shí)QueueJmsTemplate#sendAndReceive方法可以用來(lái)模擬RPC調(diào)用過(guò)程,內(nèi)部原理是:
A程序創(chuàng)建一個(gè)臨時(shí)Queue作為接受相應(yīng)的Queue
send一個(gè)Message到Queue,并在這個(gè)臨時(shí)Queue上等待結(jié)果
B程序consume了這個(gè)Message把結(jié)果send到那個(gè)臨時(shí)Queue
A接受到結(jié)果,把這個(gè)臨時(shí)Queue干掉
當(dāng)然整個(gè)過(guò)程中還包括前面提到的創(chuàng)建Connection,Session,Producer,Consumer的動(dòng)作。
不出所料,Artemis官方文檔也提到了,頻繁創(chuàng)建臨時(shí)Queue是一種anti-pattern,會(huì)大大影響性能。
@JmsListener性能問(wèn)題 使用sync方法consume消息使用@JmsListener注解可以很方便的用來(lái)consume消息,但它是同步而非異步,這個(gè)和官方文檔所說(shuō)的恰恰相反(關(guān)于sync/async consume消息的資料)。
Spring Boot的JmsAnnotationDrivenConfiguration默認(rèn)使用DefaultJmsListenerContainerFactory生成DefaultMessageListenerContainer ,而它的內(nèi)部原理是使用TaskExecutor發(fā)起多個(gè)線(xiàn)程同時(shí)從Queue中拉取消息,這也就是為什么Spring官方文檔里說(shuō)如果監(jiān)聽(tīng)的是Topic且concurrency > 1,那么可能會(huì)收到重復(fù)消息的原因。
DefaultMessageListenerContainer的javadoc中說(shuō)道:
Actual MessageListener execution happens in asynchronous work units which are created through Spring"s TaskExecutor abstraction
這里就有個(gè)矛盾,如果使用async的方式consume消息,那么只需給consumer設(shè)置MessageListener就行了,何必使用TaskExecutor呢?
一看代碼果然不出所料:
DefaultMessageListenerContainer#runcallinvokeListener
然后callAbstractPollingMessageListenerContainer#receiveAndExecute
然后calldoReceiveAndExecute
然后callreceiveMessage
然后callreceiveFromConsumer
然后callJmsDestinationAccessor#receiveFromConsumer這個(gè)方法調(diào)用了MessageConsumer#consume
也就是說(shuō)Spring只是使用一個(gè)或多個(gè)線(xiàn)程在不停的同步的consume消息而已。
雖然可以使用concurrency參數(shù)提高并發(fā),但是多線(xiàn)程從Queue/Topic中consume消息的性能比javax.jms.MessageConsumer#setMessageListener的方法要低上很多。
有興趣的同學(xué)可以使用Artemis Example(下載地址)里的JMS Perf代碼做一下測(cè)試,它的測(cè)試代碼用的是javax.jms.MessageConsumer#setMessageListener的方式來(lái)consume消息的,在配置正確的情況下可以達(dá)到接近10w/s。至于使用MessageConsumer.receive的性能測(cè)試則留給同學(xué)自己做了。
為@JmsListener創(chuàng)建的session默認(rèn)transacted=true還是之前提到的JmsAnnotationDrivenConfiguration,使用的DefaultJmsListenerContainerFactoryConfigurer默認(rèn)是把session設(shè)置為transacted的。
根據(jù)測(cè)算,當(dāng)一個(gè)session是transacted的時(shí)候其性能會(huì)相差20%,有興趣的同學(xué)可以使用Artemis Example(下載地址)里的JMS Perf代碼做一下測(cè)試。
下載之后找到examples/jms/perf目錄,看這個(gè)目錄下的readme.html獲得執(zhí)行方法,在執(zhí)行之前修改src/main/resources/perf.properties文件,下面是部分參數(shù)的解釋?zhuān)?/p>
num-messages,測(cè)試多少個(gè)消息
num-warmup-messages,熱身用的消息數(shù),熱身過(guò)之后性能會(huì)提升
durable,對(duì)應(yīng)DeliveryMode
transacted,是否transacted
batch-size,批量commit的大小
drain-queue,是否測(cè)試前先把隊(duì)列里已有的消息都清空
可以使用以下兩套配置對(duì)比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創(chuàng)建的session默認(rèn)加入了事務(wù)控制
關(guān)于加入事務(wù)控制是否會(huì)有性能問(wèn)題沒(méi)有實(shí)際測(cè)試過(guò),不過(guò)值得注意的這是Spring Boot的默認(rèn)行為。
相關(guān)連接:代碼1, 代碼2,代碼3,Javadoc。
Spring Boot配置 ConnectionFactory全局只有一個(gè)實(shí)例Spring將JMS的集成變得非常簡(jiǎn)單,只需提供幾個(gè)配置參數(shù)就可以了,但是只能提供一種默認(rèn)配置,不管業(yè)務(wù)場(chǎng)景如何都使用同一種配置是非常有問(wèn)題的。
spring-boot提供了以下幾種方式(文檔)集成JMS:
JNDI
Artemis, native模式和embedded模式
ActiveMQ
這幾種模式的缺點(diǎn)都是只能配置一個(gè)ConnectionFactory,這對(duì)于簡(jiǎn)單應(yīng)用來(lái)說(shuō)沒(méi)問(wèn)題,對(duì)于復(fù)雜應(yīng)用來(lái)說(shuō)就顯得不夠用了,這讓你喪失了針對(duì)不同場(chǎng)景配置不同ConnectionFactory的機(jī)會(huì)。
而且Artemis的native模式只支持host:port,不支持更豐富參數(shù)的url模式。
看過(guò)DefaultJmsListenerContainerFactoryConfigurer和JmsAnnotationDrivenConfiguration的代碼就明白了。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/64939.html
摘要:除此之外,還為不同的應(yīng)用程序體系結(jié)構(gòu)提供了基礎(chǔ)支持,包括消息傳遞事務(wù)數(shù)據(jù)和持久性以及,它還包括基于的框架,以及與之并行的反應(yīng)性框架。還支持依賴(lài)項(xiàng)注入和公共注解規(guī)范,應(yīng)用程序開(kāi)發(fā)人員可以選擇使用這些規(guī)范,而不是提供的特定于的機(jī)制。 概述 Spring使創(chuàng)建Java企業(yè)應(yīng)用程序變得很容易,它提供了在企業(yè)環(huán)境中使用Java語(yǔ)言所需要的一切,支持Groovy和Kotlin作為JVM上的替代語(yǔ)言...
摘要:地址前面一個(gè)部分講解了如何使用工具來(lái)測(cè)試項(xiàng)目,現(xiàn)在我們講解如何使用工具來(lái)測(cè)試項(xiàng)目。所以我們可以利用這個(gè)特性來(lái)進(jìn)一步簡(jiǎn)化測(cè)試代碼。因?yàn)橹挥羞@樣才能夠在測(cè)試環(huán)境下發(fā)現(xiàn)生產(chǎn)環(huán)境的問(wèn)題,也避免出現(xiàn)一些因?yàn)榕渲貌煌瑢?dǎo)致的奇怪問(wèn)題。 Github地址 前面一個(gè)部分講解了如何使用Spring Testing工具來(lái)測(cè)試Spring項(xiàng)目,現(xiàn)在我們講解如何使用Spring Boot Testing工具來(lái)測(cè)...
摘要:會(huì)話(huà)管理一直是企業(yè)級(jí)應(yīng)用的重要部分。傳統(tǒng)會(huì)話(huà)管理技術(shù)的問(wèn)題的目的是解決傳統(tǒng)的會(huì)話(huà)管理技術(shù)的各種問(wèn)題。對(duì)如和之類(lèi)的閉源產(chǎn)品,找到適合它們的會(huì)話(huà)管理技術(shù)的替代實(shí)現(xiàn)則通常是不可能的。典型的應(yīng)用會(huì)將當(dāng)前用戶(hù)的身份及其安全級(jí)別或角色存儲(chǔ)在會(huì)話(huà)里面。 歡迎大家前往騰訊云+社區(qū),獲取更多騰訊海量技術(shù)實(shí)踐干貨哦~ 本文來(lái)自云+社區(qū)翻譯社,由Tnecesoc編譯。 會(huì)話(huà)管理一直是 Java 企業(yè)級(jí)應(yīng)用的...
摘要:基于工廠,會(huì)有多種應(yīng)用上下文的實(shí)現(xiàn)的模塊在模塊中,面向切面編程提供了豐富的支持,該模塊是應(yīng)用系統(tǒng)中開(kāi)發(fā)切面的基礎(chǔ),可以幫助應(yīng)用對(duì)象解耦。的主頁(yè)安全對(duì)于許多應(yīng)用都是一個(gè)非常關(guān)鍵的切面。 簡(jiǎn)化Java開(kāi)發(fā) JavaBean:Enterprise JavaBean、EJBJDO:Java數(shù)據(jù)對(duì)象、Java Data ObjectPOJO:Plain Old Java ObjectDI:依賴(lài)注...
閱讀 3292·2021-11-18 10:02
閱讀 1494·2021-10-12 10:08
閱讀 1307·2021-10-11 10:58
閱讀 1311·2021-10-11 10:57
閱讀 1213·2021-10-08 10:04
閱讀 2165·2021-09-29 09:35
閱讀 810·2021-09-22 15:44
閱讀 1311·2021-09-03 10:30