activeMq的面試題

轉載自https://blog.csdn.net/belvine/article/details/79399798

一、activemq服務器宕機怎麼辦?

        這得從activemq的存儲機制說起。在通常的情況下,非持久化消息是存儲在內存中的,持久化消息是存儲在文件中的,他們的最大限制在配置文件的<systemUsage> 節點中配置。但是在非持久化消息堆積到一定程度,內存告急的時候,activemq會將內存中的非持久化消息寫入臨時文件中,以騰出內存。

       雖然都保存到了文件裏,但它和持久化消息的區別是:重啓後持久化消息會從文件中恢復,非持久化的臨時文件會直接刪除。

       那如果文件增大到達了配置中的最大限制的時候會發生什麼?我做了以下實驗:

       1. 設置2G左右的持久化文件限制,大量生產持久化消息直到文件達到最大限制,此時生產者阻塞,但消費者可正常連接並消費消息,等消息消費掉一部分,文件刪除又騰出空間之後,生產者又可繼續發送消息,服務自動恢復正常。

      2. 設置2G左右的臨時文件限制,大量生產非持久化消息並寫入臨時文件,在達到最大限制時,生產者阻塞,消費者可正常連接但不能消費消息,或者原本慢速消費的消費者,消費突然停止。整個系統可連接,但是無法提供服務,就這樣掛了。

       解決方案:儘量不要使用非持久化消息,非要用的話,將臨時文件限制儘可能的調大。

二、丟消息怎麼辦

    這得從java的java.net.SocketException異常說起。簡單點說就是當網絡發送方發送一堆數據,然後調用close關閉連接之後。這些發送的數據都在接收者的緩存裏,接受者如果調用read方法仍舊能從緩存中讀取這些數據,儘管對方已經關閉了連接。但是當接收者嘗試發送數據時,由於此時連接已關閉,所以會發生異常,這個很好理解。不過需要注意的是,當發生SocketException後,原本緩存區中的數據也作廢了,此時接收者再次調用read方法讀取緩存中的數據,就會報Software caused connection abort: recv failed錯誤。

       通過抓包得知,Activemq會每隔10秒發送一個心跳包,這個心跳包是服務器發送給客戶端的,用來判斷客戶端死沒死。看完上一條,就會知道非持久化消息堆積到一定程度會寫到臨時文件中,這個寫的過程會阻塞所有動作,而且會持續20到30秒,並且隨着內存的增大而增大。當客戶端發完消息調用connection.close()時,會期待服務器對於關閉連接的回答,如果超過15秒沒回答就直接調用socket層的close關閉tcp連接了。這時客戶端發出的消息確實還在服務器的緩存裏等待處理,不過由於服務器心跳包的設置,導致發生了java.net.SocketException異常,把緩存中的數據作廢了,沒處理的消息全部丟失。

      解決方法:用持久化消息,或者非持久化消息及時處理不要堆積,或者啓動事務。啓動事務後,commit()方法會負責任的等待服務器的返回,也就不會關閉連接而導致消息丟了。

三、持久化消息非常慢

      默認的情況下,非持久化的消息是異步發送的,持久化的消息是同步發送的,遇到慢一點的硬盤,發送消息的速度是無法忍受的。但是在開啓事務的情況下,消息都是異步發送的,效率會有2個數量級的提升,請務必開啓事務模式。其實發送非持久化消息時也建議開啓事務,因爲根本不會影響性能。

四、消息的不均勻消費

     有時在發送一些消息之後,開啓了兩個消費者去處理消息。會發現一個消費者處理了所有的消息,另一個消費者根本沒收到消息。原因在於activemq的prefetch機制。當消費者去獲取消息時,不會一條一條企業獲取,而是一次性的獲取一批,默認是1000條。這些預獲取的消息,在還沒確認消費之前,在管理控制檯還是可以看見這些消息的,但是不會再分配給給其他消費者,此時這些消息的狀態應該算作“已分配爲消費”,如果消息最後被消費,則會在服務器被刪除,如果消費者崩潰,則這些消息會被重新分配給新的消費者。但是如果消費者既不消費確認,又不崩潰,那這些消息就永遠躺在消費者的緩衝區裏無法處理。更通常的情況是,消費這些消息非常耗時,你開了10個消費者去處理,結果發現只有一臺機器處理,另外九臺啥事不幹。

      解決方案:將prefetch設爲1,每次處理一條消息,處理完再去取,這樣也慢不了多少。

五、死信隊列

     如果你想在消息處理失敗後,不被服務器刪除,還能被其他消費者處理或者重試,可以關閉AUTO_ACKNOWLEDGE,將ack交由程序自己處理。那如果使用了AUTO_ACKNOWLEDGE,消息是什麼時候確認的,還有沒有阻止消息確認的方法?有!

    消費消息有2種方法:一種是調用consumer.receive()方法,該方法將阻塞直到獲得並返回一條信息。這種情況下,消息返回非方法調用者之後就自動被確認了。另一種方法就是採用listener回調函數,在有消息到達時,會調用listener接口的onMessage方法。在這種情況下,在onMessage方法執行完畢後,消息纔會被確認,此時只要在方法中拋出異常,該消息就不會被確認。

    那麼問題來了,如果一條消息不能被處理,UI被退回到服務器重新分配,如果只有一個消費者,該消息又重新被獲取,重新拋異常。就算有多個消費者,往往在一個服務器上不能處理的消息,在另外的服務器上依然不能被處理。難道就這麼退回-獲取-報錯死循環嗎?

     在重試6次後,ActiveMQ認爲這條消息是“有毒”的,將會把消息丟到死信隊列裏。如果你的消息不見了,去ActiveMQ.DLQ裏找找,說不定就躺在那裏面。

六、ActiveMQ中有消息重發時間間隔和重發次數嗎?

     ActiveMQ:是Apache出品,最流行的,能力強勁的開源消息總線。是一個完全支持JMS1.1和j2ee 1.4規範的JMS Provider實現。

    JMS(java消息服務):是一個java平臺中關於面向消息中間件(MOM)的API,用於在兩個應用程序之間,或分佈式系統中發送消息,進行異步通信。

     首先,我們的大概瞭解下,在哪些情況下,ActiveMQ服務器會將消息發給消費者,這裏爲簡單起見,假定採用的消息發送模式爲隊列(即消息發送方和消息接收方)。

    1.如果消息接收者在處理完一條消息的處理過程後沒有對MOM進行應答,則該消息將由MOM重發

    2.如果我們隊某個隊列設置了預讀參數(consumer.prefetchSize),如果消息接收者在處理第一條消息時(沒向MOM發送消息接收確認)就宕機了,則預讀數量的所有消息都將被重發!

    3.如果Session是事務的,則只要消息接收方有一條消息沒有確認接收,或發送消息期間MOM或客戶端某一方突然宕機,則該事務範圍中的所有消息MOM都將重發。

    4.說到這裏,大家可能會有疑問,ActiveMQ消息服務器怎麼知道消費者客戶端是消息正在處理中還沒來得急對消息進行應答還是已經處理完成了沒有應答或是宕機了根本沒機會應答呢?其實在所有的客戶端機器上,內存中都運行着一套客戶端的ActiveMq環境,該環境負責緩存發來的消息,賦值維持着和ActiveMQ服務器的消息通訊,負責失效轉移(fail-over)等,所有的判斷和處理都是由這套客戶端環境來完成的。

       我們可以對ActiveMq的重發策略(Redelivery Policy)來進行自定義配置,其中的配置參數主要有以下幾個:

      

 可用屬性
屬性 默認值 說明
collisionAvoidanceFactor 0.15 設置防止衝突範圍的正負百分比,只有啓用useCollisionAvoidance參數時才生效。
maximumRedeliveries 6 最大重傳次數,達到最大重連次數後拋出異常。爲-1時不限制次數,爲0時表示不進行重傳。
maximumRedeliveryDelay  -1 最大傳送延遲,只在useExponentialBackOff爲true時有效(V5.5),假設首次重連間隔爲10ms,倍數爲2,那麼第二次重連時間間隔爲 20ms,第三次重連時間間隔爲40ms,當重連時間間隔大的最大重連時間間隔時,以後每次重連時間間隔都爲最大重連時間間隔。
initialRedeliveryDelay 1000L 初始重發延遲時間
useCollisionAvoidance false 啓用防止衝突功能,因爲消息接收時是可以使用多線程併發處理的,應該是爲了重發的安全性,避開所有併發線程都在同一個時間點進行消息接收處理。所有線程在同一個時間點處理時會發生什麼問題呢?應該沒有問題,只是爲了平衡broker處理性能,不會有時很忙,有時很空閒。
useExponentialBackOff false  啓用指數倍數遞增的方式增加延遲時間。
 backOffMultiplier  5 重連時間間隔遞增倍數,只有值大於1和啓用useExponentialBackOff參數時才生效。 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章