程式碼概觀
架構
以下章節將逐步介紹 Apache ActiveMQ Classic 的主要部分,並連結到程式碼,以幫助您了解其佈局
JMS 用戶端
org.apache.activemq 套件定義了核心 JMS 用戶端。
傳輸
JMS 用戶端和訊息代理程式使用 Transport 抽象概念來傳送命令物件(類似於分散式的命令模式)。TransportChannel 通常處理某種網路機制(使用 BIO 的 TCP 通訊端、使用 NIO、UDP / 多播、透過通訊端的 SSL、JXTA、EmberIO 等)。詳情請參閱 org.apache.activemq.transport 套件
因此,TransportChannel 主要負責傳送和接收 Command 物件(每個實例代表某種命令)。封包在 org.apache.activemq.command 套件中定義,該套件定義了所有 JMS 訊息實作類別(即命令),以及許多其他類型的封包,例如訂閱、訊息確認、交易等等。
線路格式
有多種方法可以將訊息編碼到串流中。我們可能希望適應各種不同的編碼機制 - 例如,為與 C / JavaScript 對話提供更簡單的線路格式,或製作 C# 友好的編碼。
因此,所有 Transport 實作都採用可插入的 WireFormat 實作類別 - 這是用於決定如何將 Command 寫入 DataIn / DataOut 串流或資料報的策略模式。
因此,如果您希望提供自己的二進位線上協定,那麼我們只需要您的協定的 WireFormat 實作,然後我們可以將其與任何傳輸(TCP BIO、NIO、JXTA 等)一起使用。
我們預設使用 OpenWireFormat,這是 Java 程式碼中使用最有效率且最簡單的格式 - 因此,如果線路的兩端都是 Java,則強烈建議使用。不過,我們非常歡迎其他 WireFormat。
預設線路格式
預設線路格式會寫入一個位元組,表示要傳送的 Command 類型(請參閱 CommandTypes 介面,該介面定義了每種命令類型的所有 int 常數)。
核心 JMS 訊息類型各有唯一的位元組 ID,適用於
- Message
- ObjectMessage
- TextMessage
- MapMessage
- BytesMessage
- StreamMessage
此外,還有其他各種 命令類型,例如
- ConnectionInfo,用於當與訊息代理程式建立新連線時
- ConsumerInfo,用於在連線上建立新的消費者時
- MessageAck,用於確認訊息 ID
- TransactionInfo,用於表示交易
還有其他一些;org.apache.activemq.command 套件會詳細描述它們。
基本上,DefaultWireFormat 對每個命令都有預設編碼。因此,在寫入表示封包類型的第一個位元組之後,每個封包類型都有特定的線路格式。
對於新的線路格式,您可能只需要支援這些類型的一小部分。例如,您可能只有一個簡單的發佈訊息、取用訊息和訊息確認。
訊息代理程式
訊息代理程式(JMS 用戶端的伺服器端)的 API 在 org.apache.activemq.broker 中定義。還有其他各種套件定義了不同的部分,從訊息儲存到訊息路由等等。
若要查看這些套件的概觀,請嘗試 JavaDocs
ActiveMQ Classic 系統概觀
簡介
ActiveMQ Classic 是負責建立和管理用戶端與代理程式之間通訊所用網路連線的系統。本文檔旨在概述此系統的內部運作方式,以便讓未來的開發人員更容易理解。它將對系統進行高階概述,並概述主要參與者。我們還將介紹一些其他可能對系統開發人員有用的有趣類別。本文檔的大部分內容都是以伺服器端程式碼為考量撰寫的。這是因為用戶端通訊系統的架構很簡單,而且了解伺服器會讓了解用戶端變得微不足道。
我們假設讀者對 JMS 有基本的了解。如需詳細資訊,請參閱官方 Java 文件。
概觀:主要參與者
ActiveMQ Classic 通訊系統中涉及的核心類別是傳輸。這些類別包括 Transport
、TransportServer
和 TransportFactory
階層。Transport
和 TransportServer
分別是通訊端和伺服器通訊端的包裝函式。TransportFactory
(您可能猜到了)是建立 Transport
和 TransportServer
的工廠。Transport
連接到 Broker
並傳輸 Command
,它代表 ActiveMQ Classic 要執行的所有主要動作(稍後會詳細介紹)。以下範例說明這些部分如何結合在一起。
建立 JMS「提供者」應用程式所需的主要類別是 Broker
類別。預設的 ActiveMQ Classic 二進位檔會使用 BrokerService
類別來包裝 Broker
。當應用程式啟動時,它會實例化一個 BrokerService
,並指示它繫結到特定的(本機)位址,例如「tcp://127.0.0.1:61616」。Broker
會使用給定位址中的配置,並找到正確的 TransportFactory
,在此範例中為 TcpTransportFactory
。然後,這個工廠會用於建立將繫結到「localhost:61616」的 TcpTransportServer
。一旦 TransportServer
啟動,它會持續輪詢其通訊端以取得傳入的連線。成功連接的傳入通訊端將會包裝在 TcpTransport
實例中,並(間接)傳回給 Broker
。Broker
接著會開始輪詢新的 Transport
,以取得要處理的傳入 Command
。
上述範例中遺失的最後一部分是 TransportConnection
和 TransportConnector
類別。這些類別用於將 Broker
分別連接到 Transport
和 TransportServer
。
類別詳細資料
本節將分別說明上述類別的一些更有趣的詳細資料。
傳輸、TransportServer 和 TransportFactory
這些類別的運作基本原理非常簡單:Transport
和 TransportServer
是通訊端和伺服器通訊端的包裝函式,用於隱藏實作,而 TransportFactory
是上述類別的工廠類別。唯一的注意事項是 TransportFactory
如何根據它們提供的 URI 來選擇和配置。
TransportFactory
類別是抽象的,無法直接建立 Transport
或 TransportServer
類別。不過,它仍然是用來建立 Transport
和 TransportServer
的類別。TransportFactory
會將其職責委派給其子類別,這是根據 FactoryFinder
類別提供的子類別選擇來委派的,該類別會使用 URI 的配置,根據儲存在 META-INF 目錄下的文字檔來尋找相符的工廠類別。
所建立的 Transport
的配置完全是透過反射來完成的。Transport
是透過呼叫 compositeConfigure
來配置的,這些呼叫是由工廠在建立 Transport
時進行的。compositeConfigure
使用 IntrospectionSupport
類別來呼叫 URI 中傳入參數的 setter。例如,使用 URI「ssl://127.0.0.1:61616/?needClientAuth=true」建立 Transport
會導致建立一個 SslTransport
物件,其 setNeedClientAuth
方法(如果存在)會在建立後立即呼叫並使用值 true
。TransportServer
的運作方式類似。唯一的差異是,對 IntrospectionSupport
的呼叫是從 TransportFactory
的 doBind
方法發出的。
命令
Command
是 Broker
中通訊的主要方式。每個 Command
代表要執行的動作。Command
子類別包括 ConnectionInfo
、KeepAliveInfo
和 Message
,它們會導致處理新連線、維護舊連線以及處理使用者訊息。這些類別是使用封送處理器從 Transport
取消序列化的。每當在通訊端中找到新資料時,會讀取第一個位元組,以判斷接收的 Command
類型。然後,會選取適當的封送處理器來取消序列化 Command
(例如,若要取消序列化 ConnectionInfo
,會使用 ConnectionInfoMarshaller
)。
TransportConnections 和 TransportConnectors
每一個 TransportServer
都會使用一個 TransportConnector
連接到一個 Broker
。伺服器的接受監聽器 (當一個新的 Transport
被建構時會被呼叫) 設定為呼叫給定的 TransportConnector
的 createConnection
方法,並帶入新的 Transport
。當被呼叫時,createConnection
會建立一個新的 TransportConnection
,將給定的 Transport
和支援的 Broker
連接在一起;Transport
的傳輸監聽器會設定為 TransportConnection
的 onCommand
方法,該方法會在收到新的 Command
時被呼叫。
Command
和 AbstractConnection
(TransportConnection
的父類別) 形成一個訪問者模式。onCommand
會呼叫 AbstractConnection
的 service 方法,該方法會根據訪問者模式進行一系列的呼叫,最終將適當的 Command
子類別傳遞給 Broker
的相應方法進行處理。
BrokerFilters 和 BrokerPlugins
雖然通訊系統不直接使用 BrokerFilter
和 BrokerPlugin
,但它們提供了一種有效且易於使用的方法來修改 Broker 的行為。BrokerFilter
允許修改一些 Broker
的方法,而無需觸及其他部分 (顧名思義)。BrokerFilter
將其所有職責傳遞給在其建構函式中接收到的 Broker
。繼承 BrokerFilter
允許我們在將工作傳遞給底層的 Broker
之前執行其他操作。
BrokerFilter
類別的強大之處在於可以串聯多個篩選器來創建不同的功能組合。例如,JaasAuthenticationBroker
是 BrokerFilter
的一個子類別,它修改用於新增和移除連線的方法,以允許 JAAS 身份驗證。AuthorizationBroker
是 BrokerFilter
的另一個子類別。這個類別修改目標規則方法來強制執行存取層級。透過這種架構,可以建立一個 JaasAuthenticationBroker
,並讓它使用一個 AuthorizationBroker
作為其底層的 Broker (而該 Broker 本身又會使用另一個 Broker 等等)。
BrokerPlugin
是一個簡單的類別,它會將其對應的 Broker
包裝在其所給定的 Broker
周圍。也就是說,在現有的 Broker
上「安裝」一個 AuthorizationPlugin
將會建立一個 AuthorizationBroker
,它在內部使用原始的 Broker
。 BrokerPlugin
存在的主要原因是允許使用者設定 BrokerService
類別使用的 Broker
(透過程式碼或 XML 設定以及 Spring)。