spring-jms的接收消息功能的設計思考

1、前言

JMS即Java消息服務,面向消息中間件的API。本文研究過源碼,試着分析一下如何從基本的使用方式,到整合進spring的方式的思考,來提高自己的系統設計能力。
只分析消息接收處理過程,話說spring-jms是整合jms的使用,而springcloud-stream是自己實現了一個發送接收過程,通過設計binding對接消息中間件上。

本文分析的源碼是在spring-framework中的spring-jms模塊中。先是分析了消息接收相關的連接與消息監聽的關聯關係,再分析了這種關聯產生一個消息處理容器,最後分析如何讓一個後置處理器來產生這樣的功能結構。

最近補上核心功能類,消息監聽容器ListenerContainer的設計分析

2、jms接收消息

jsm是標準接口,具體的產品要實現 連接工廠ConnectionFactory、Connection、Session、MessageConsumer等接口。通常我們的使用方式如圖上所示。用戶真正關心的是連接哪個消息中間件,消息處理邏輯。
在這裏插入圖片描述

3、初步設計

一個消息中間件上,可以有多個消息處理,分別接收不同目的地的消息。而系統考慮多個消息中間件。
初步的設計如上圖的虛線所示的類,包括:

  1. 持有當前JMS產品連接工廠的類A:可以有多個,每個都是spring容器中的默認單例bean,還可以設置一些連接相關的屬性。

  2. 包含消息處理方法的類B:每個消息的處理就是類的一個方法,要註解,標識對應哪個JMS產品(A),對應哪個目的地。

  3. 每一個消息處理方法都拆解出來,與相對應的JMS產品配置,成爲一個組合©。在這個組合中,一但組合啓動,可以產生或共享connection,產生session,consummer,有一個線程一直輪詢,拿到消息,可以反射調用消息處理方法。利用spring的bean生命週期進行作業線程的啓動與停止。

  4. C類會產生很多,需要D類管理起來

  5. 利用spring的擴展點進行組裝,於是需要一個後置處理器類E

    有了上面的設計,可以利用上圖下面部分的虛線的類,開發出一個簡單的系統,運行起來。

4、重構

本着功能單一,界限明確的原則和一些常用的套路。 一些配置與功能是要分開的,配置類往往是功能類的工廠。通常產生的一批對象,是要統一進行管理的。

4.1 類的重構

  1. A是持有JMS產品的連接工廠的,同一個JMS產品的連接下,可能有多個destination的消息的接收,主要由用戶配置。就是一個持有JMS產品連接工廠的類,暫時無切分/合併要求。最後叫:ListenerContainerFactory。配置類叫工廠也是合理的。
  2. B是每一個註解的方法,所以應該有一個類把方法、註解信息、所在的類,對應的JMS產品,相應的destination都記錄下來。最後是叫:Endpoint
  3. A與B的個數不定,A與B一般是一對多的關係的,必須有一個關聯,可以是B關聯A,因爲註解上也是這樣。這樣的一組組關聯必然記錄在一個單例對象中,這就是C。這個一組AB叫:descriptor
  4. AB組合會產生一個工作單元,是動態的內容,也放在C中是可以的。不過這一組靜態屬性,一個動態工作單元明顯不同,C可以拆成兩個類C1/C2,分別管理靜態與動態部分。最後C1叫:Registrar,C2叫:Registry
  5. 先有C1,後有C2,C2存着所有的動態單元,每個單元都有啓停操作,正好由C2統一啓停,C2可以實現spring的lifeCycle接口。
  6. 一個個動態單元稱爲listenerContainer,肯定由配置類來生成。由誰產生,又在什麼時間生成呢?A可以,B可以,時間可以是找到B產生配置組時,也可以在所有的配置組都找到了統一再產生。源碼是由A產生,所以A叫ListenerContainerFactory。A正好還存放所有listenerContainer公共用的對象,連接工廠,事務管理器,公共線程池等。
  7. 最後,所有的關係需要由spring的後置處理器來完成,這就是JmsListenerAnnotationBeanPostProcessor,由@EnableJms引入到spring中。在處理前,spring容器中應該有用戶提供在config類中相關的配置bean,分別是JMS連接工廠,ListenerContainerFactory,以及方法上有@JmsListener註解的類。

4.2 後置處理器BeanPostProcessor處理過程

  1. 找出所有的@listener的方法,產生Endpoint。以及根據註解中的ListenerContainerFactory名字,從spring容器中找到對象。
  2. Endpoint與ListenerContainerFactory組成一組,放置在Registrar中統一管理。
  3. 用其管理類Registrar的方法,上面每產生的一組,都產生ListenerContainer對象,放置在Registry的map中統一管理。
  4. Registrar與Registry都是單例,後置處理器也是單例,它們之間要有引用。後置處理器BeanPostProcessor中有Registrar,Registrar中有Registry,它們不需要反向引用。源碼中Registrar是BeanPostProcessor所new出來的,BeanPostProcessor又aware下spring容器,從中拿到Registry,並設置給Registrar用。爲何不都在@EnableJms中引入並關聯好呢?

4.3 listenerContainer的說明

簡單的話可以是一個線程polling消息,但可以是多線程,所以可以共享連接,但session,consummer每個線程都是不一樣的。所以包含了一個內部類AsyncMessageListenerInvoker,每個實例對象持有自己的相關對象,它也是runable接口。
即然是多個,也要被listenerContainer統一管理。具體的線程數控制,線程的等待與通知管理還是比較複雜的,本文後面第6章節補上了詳細設計分析

5、源碼中的設計

下面的圖,簡單彙總了源碼相關的類中的引用與功能。與標準的類圖不同,這裏簡化了細節,只爲記在大腦中,設計開發時思路連貫,不偏離大方向。
在這裏插入圖片描述

6、核心功能類ListenerContainer的設計分析

每一個註解的@jsmListener將由工廠產生一個ListenerContainer,這裏核心功能類,實現了特定消息中間件,特定目的地,特定消息處理器下,多線程獲取消息並處理消息的過程,這一切都納入spring容器的生命週期管理。

  • 如圖所示:設計的層次感很好,這個類多層繼承過來,並實現了多個接口。上兩層是特定消息中間件連接工廠和目的地。接下來是比較抽象的生命週期管理過程。接着是消息的處理,消息的輪詢獲取,最後是多線程管理。
  • 層次感比如:消息處理是獲取消息的上層,因爲輪詢可以換push,但消息處理可以不變。同樣是輪詢,底層的多線程管理可也是換成其它方式。
    在這裏插入圖片描述
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章