Artemis架構解析

前言
    Artemis是一款基於Netty NIO的高性能消息中間件,它的前身爲JBoss的HornetQ,2015捐獻給了Apache ActiveMQ社區,並命名爲Apache Artemis。shell

    本文將對Artemis的架構作一個簡單的解析,將Artemis的架構拆分爲兩塊:Artemis Broker的解析(也能夠說是單點實例的Artemis消息代理內部運行架構解析)和Artemis高可用架構解析。apache

1.Artemis Broker
    以下圖爲Artemis Broker的架構圖,Artemis Broker按其所提供的核心功能及外部工具與接口兩部分構成。將Broker作出這樣的劃分實際上爲了本文的結構考慮,核心功能和外部工具與接口兩部分都是Artemis Broker的組成部分。瀏覽器

1.1 外部工具與接口
1.1.1 命令行工具
    Artemis爲用戶提供了功能豐富的命令行工具,用戶能夠經過運行Artemis的${INSTANCE}/bin目錄下的Artemis腳本加上對應的參數來調用命令行工具,如運行Artemis Broker實例;緩存

    ${INSTANCE}/bin/Artemis run安全

    Artemis命令行工具構建於Airline之上,Airline是一個基於Java註解解析命令行結構的框架。Artemis中全部的命令工具都實現org.apache.activemq.artemis.cli.commands.Action接口。基於Action的命令行工具層級結構:性能優化

HelpAddress (address組的幫助)
HelpData (data組的幫助)
Mask (屏蔽密碼操做)
HelpQueue (queue組的幫助命令)
ActionAbstract (org.apache.activemq.artemis.cli.commands)
    Migrate1X (遷移1.x版本配置命令)
    XmlDataImporter (導入XML消息數據命令)
    InputAbstract (org.apache.activemq.artemis.cli.commands)
        ConnectionAbstract (org.apache.activemq.artemis.cli.commands.messages)
            AbstractAction (org.apache.activemq.artemis.cli.commands)
                QueueAbstract (org.apache.activemq.artemis.cli.commands.queue)
                    UpdateQueue (更新一個隊列命令)
                    CreateQueue (建立一個隊列或topic命令)
                DeleteQueue (刪除隊列命令)
                AddressAbstract (org.apache.activemq.artemis.cli.commands.address)
                    ShowAddress (獲取地址信息命令)
                    CreateAddress (建立地址命令)
                    UpdateAddress (更新地址命令)
                    DeleteAddress (刪除地址命令)
                StatQueue (打印出與隊列相關的基本統計數據命令)
            DestAbstract (org.apache.activemq.artemis.cli.commands.messages)
                Browse (瀏覽指定實例消息命令)
                Producer (發送消息到指定實例命令)
                Consumer (消費指定實例命令)
        UserAction (org.apache.activemq.artemis.cli.commands.user)
            PasswordAction (org.apache.activemq.artemis.cli.commands.user)
                ResetUser (重置用戶密碼或角色命令)
                AddUser (添加一個新用戶命令)
            RemoveUser (刪除已存在用戶命令)
            ListUser (列出已存在用戶命令)
        Create (建立Artemis實例命令)
    Configurable (org.apache.activemq.artemis.cli.commands)
        Stop (中止Artemis實例命令)
        Kill (殺死以--allow-kill方式啓動的Artemis實例命令)
        DataAbstract (org.apache.activemq.artemis.cli.commands.tools)
            LockAbstract (org.apache.activemq.artemis.cli.commands.tools)
                EncodeJournal (爲一組日誌文件的編碼設置爲內部數據格式命令)
                Run (運行Artemis實例命令)
                OptionalLocking (org.apache.activemq.artemis.cli.commands.tools)
                    PerfJournal (計算如今使用磁盤的journal-buffer-timeout設置的建議值命令)
                    DBOption (org.apache.activemq.artemis.cli.commands.tools)
                        PrintData (打印數據記錄信息命令(注意:在生產服務器運行時不要使用))
                        XmlDataExporter (以XML格式導出全部消息數據命令)
                CompactJournal (壓縮非運行服務器的日誌命令)
                DecodeJournal (將日誌的內部格式解碼爲新的日誌文件集命令)
HelpUser (user組的幫助命令)
HelpAction (全局幫助命令)

 1.1.2 RESTful API
    Artemis REST接口容許經過簡單的REST/HTTP接口來調用Artemis功能。Artemis的REST接口是基於Artemis JMS API之上構建的,也就是說REST接口其實暴露的是JMS的功能。使用Artemis REST接口可使用大部分JMS消息功能,如能夠很是簡單的經過發送和接收HTTP消息來實現生產和消費功能,或者基於JMS消息的管理接口。服務器

   Artemis REST接口依賴RESTEasy項目構建,部署時須要將其打成WAR包,只能容許在Servlet容器中。安裝Artemis REST接口按照其是嵌入式(如在Wildfly中部署),仍是使用WAR包獨立啓動它們的安裝配置是有些區別的,須要特別注意。網絡

1.1.3 JMX
    Artemis提供了基於JMX(Java Management Extensions,Java管理擴展)的管理API,經過域'org.apache.activemq.artemis'來註冊資源,並經過MBean接口來對外提供管理API。Artemis的JMX與使用其餘Java應用提供的JMX相同,均可以經過反射或建立MBean代理來實現訪問。數據結構

1.1.4 管理控制頁面
    爲了方便用戶能直接經過頁面對Artemis進行管理和監控,Artemis提供了一個默認的管理控制檯。這個控制檯由Hawt.io提供支持的,Hawt.io是一個能夠插入式HTML5面板。Artemis的管理控制檯使用Jolokia做爲JMX中介,在本地JMX上構建基於http協議使用JSON做爲數據格式的外部接口,能夠看做是遠程訪問JMX MBean的另外一種實現方式。架構

    經過控制檯頁面,用戶能直觀的對Artemis的運行情況進行監控,如監控鏈接、會話、隊列等狀況,而且能經過接口對Artemis進行直接控制操做如配置管理、關閉隊列、建立地址等等。能夠說JMX提供的管理API在控制檯頁面很是簡單的經過瀏覽器就能調用。

1.2 Artemis核心功能
1.2.1 接入層(Acceptor)
    Artemis提供了兩種接收器:

InVMAcceptor

    InVMAcceptor是在同一JVM進程中使用的接收器,能夠在broker.xml配置文件中經過<acceptor>元素來聲明啓動一個VMAcceptor:

<acceptor name="in-vm">vm://clientId?create=false</acceptor>
    "vm:"表明要使用VMAcceptor。「clientId」爲服務器惟一ID, 也就是用於標識指定的Artemis Broker實例。

     InVMAcceptor不一樣於與咱們一般認知的基於監聽指定端口接收來自網絡的請求,它經過模擬監聽模型來建立虛擬的通信體系。一樣能創建鏈接通道,也有消費者和生產者的概念,可是須要注意的,前提條件是必須與Artemis服務器實例處於同一JVM進程中才能創建這個虛擬的鏈接通道。基於InVMAcceptor所實現的服務,其實是直接經過調用Artemis中對應的JaveBean而實現的功能。

NettyAcceptor

    NettyAcceptor是基於Netty的NIO構建,這也是Artemis高性能緣由之一。

    NettyAcceptor能夠支持單端口多協議功能,如CORE、AMQP、STOMP、MQTT等協議可使用同一個Acceptor也能夠指定不一樣的Acceptor。同一個Acceptor還能夠同時支持HTTP、Websocket協議。這都是歸功於基於ChannelHandler消息攔截與處理器,經過攔截每條消息,根據其協議交個不一樣的handler進行處理。

1.2.2 安全層(Security)
    Artemis提供了基於角色的安全模型。爲用戶的鏈接、訂閱、發送消息等操做權限提供了保障。

    Artemis經過基於JAAS(Java驗證和受權API)的安全管理器ActiveMQJAASSecurityManager進行用戶受權。對於如何獲取用戶信息取決於使用哪一種登陸模塊。

來賓登入模塊

    來賓登入模塊(GuestLoginModule)應用於沒有用戶憑證的用戶訪問Artemis。來賓模型一般會與其餘登入模型結合使用,爲來賓用戶定義一個統一的權限,好比來賓用戶只有查看權限,沒有消費和發佈消息的權限。

屬性登陸模塊

    屬性登入模型(ProperteisLoginModule)經過存儲用戶數據的屬性文件,來爲模塊提供對應的數據。Artemis默認使用屬性登入模塊來爲其提供簡單的用戶數據存儲。

    默認這兩個屬性文件分別爲:artemis-users.properties用於存儲用戶密碼對,artemis-roles.properties用來存儲指定權限對應的用戶列表。

LDAP登陸模塊

    LDAP登陸模塊(LDAPLoginModule)基於LDAP(輕量級目錄訪問協議)使用樹形結構來存儲用戶數據的。使用這個登入模塊須要部署一套X.500目錄服務器,經過LDAPLoginModule與目錄服務器創建鏈接,用此服務器中的數據與用戶的登入數據進行對比來執行身份驗證和受權動做。

    LDAPLoginModule兼容ActiveMQ 5.x的用戶數據結構,它會在容許的範圍內將用戶數據轉換成Artemis所支持的數據。

Kerberos認證

    Artmies基於Krb5LoginModule實現了Kerberos登入模塊來獲取用戶憑證,要使用Kerbeoros認證必需要先部署kerberos的基礎環境。

1.2.3 協議管理(Protocol Management)
    在代碼層面其實協議管理模塊是先於安全管理被調用的,可是安全管理有在處理具體的協議邏輯的時候被調用,好比鏈接、發送消息等。這兩個模塊按代碼層次來講實際上是糾纏在一塊兒的,很差說誰先誰後。可是按架構層面理解,是應該安全管理在前,有權限才能繼續往下繼續處理,因此這裏把安全層放在協議管理以前。   

    Artemis接入層接收到消息後,對於NettyAcceptor,它啓動Netty服務的時註冊了一個Handler,協議解析攔截器ProcolDecoder,它會解析客戶端使用何種協議與Artemis通信。

    Artemis解析完消息使用何種協議後,經過查找對應協議的ProtocolManager來註冊指定協議的編解碼處理器和協議邏輯處理器。以MQTT協議爲例,MQTTProtocolManager會爲在Netty的Handler鏈中註冊MQTT協議的編碼器MqttEncoder、×××MqttDecoder、和協議處理器MQTTProtocolHandler。如此Artemis基於ProcolDecoder的動態裝載對應協議處理Handler,實現了只需一個端口就能處理多個協議通信的功能。

1.2.4 服務層(Server)
    本文將Artemis的服務層定義爲,囊括了非協議處理特定邏輯以外全部與協議處理通用的邏輯之集合(持久化除外,之因此把持久化排查在服務層以外,是由於通常咱們在討論架構的時候一般都會把持久化層抽象爲單獨的模塊,與業務處理邏輯隔離)。

    以下將主要講述服務層的三部分功能,分別爲地址、隊列、檢測。

1.2.4.1 檢測

對於檢測需特別說明下,在代碼層次中,各種檢測功能其實分散嵌入在對應的模塊,並無在代碼層次進行獨立封裝。此處將檢測獨立做爲一個模塊來描繪,是爲了方便對各種檢測功能進行集中說明,將其嵌入進其餘模塊進行講解太過度散,讓人不太容易注意到對應的檢測功能;

     Artemis提供的檢測包含:殭屍鏈接檢測、代理健康檢測、慢消費者檢測、消息超時檢測等。

殭屍鏈接檢測    

    當客戶端崩潰或者客戶端退出可是未關閉鏈接,這類鏈接稱爲殭屍鏈接。若是服務端沒有主動的鏈接檢測,將會可能使服務端長時間持有大量的殭屍鏈接,致使服務端資源泄漏,甚至會因爲資源耗盡致使服務器崩潰。

     檢測功能由RemotingServiceImpl中的內部類FailureCheckAndFlushThread定義,FailureCheckAndFlushThread做爲一個獨立的線程定時輪詢全部的鏈接,檢查其鏈接在指定時間內是否有接收到心跳包,若是沒有斷定爲超時觸發鏈接關閉動做。

    這裏的鏈接超時時間能夠由客戶端在鏈接到Artemis Broker時,客戶端定義好做爲協議參數傳給Broker,也能夠經過Broker中配置connection-ttl-override屬性做爲全局值使用。

代理健康檢測

    爲了防止Artemis爲外部提供壞的服務,Artemis提供服務健康情況自檢功能。什麼是壞的服務呢,好比客戶端能鏈接到Artemis可是一直髮送消息超時,由於磁盤IO阻塞,致使客戶端發送的消息阻塞在持久化操做,沒法響應應答消息給客戶端。

    Artemis會在監控檢測線程中對隊列投遞、持久化操做、分頁操做進行按期測量,若是響應超時,代理會被認定爲不穩定,會根據代理中配置的超時處理策略critical-analyzer-policy來進行對應的操做。默認策略爲LOG會對超時狀況進行日誌輸出,也能夠將策略配置爲HALT暫停服務器、SHUTDOWN中止服務器。

    代理監控檢測代碼封裝在CriticalAnalyzer接口中,其實現類有兩個:EmptyCriticalAnalyzer和CriticalAnalyzerImpl,代理健康檢測功能都在CriticalAnalyzerImpl實現,對於EmptyCriticalAnalyzer是個空類,就是什麼都不作的類,僅僅只是繼承了CriticalAnalyzer接口,其方法都是什麼都不作的空方法。EmptyCriticalAnalyzer是幹什麼用的,只是做爲用戶配置關閉代理監控檢測功能時調用這個空檢測,而且爲之後可能會添加檢測功能的模塊在其初始化的時候傳入這個空檢測爲其插入對應檢測功能作預留。

消息超時檢測

    若是發送到代理中的消息有設置過時時間,消息在存活時間內還未被投遞,服務器將丟棄該消息。Artemis也支持定義一個逾期地址,消息逾期後逾期消息會被髮送到這個逾期地址中。

    逾期消息的功能代碼封裝於PostOfficeImpl中的內部類ExpiryReaper,逾期檢測初始化代理階段被開啓運行。它的邏輯很簡單就是經過地址管理獲取全部的綁定關係,從綁定中獲取隊列。遍歷全部隊列,讓各個隊列啓動本身的掃描線程來掃描隊列中的全部消息是否有逾期消息。

慢消費者檢測

    首先說下慢消費者會對消息代理形成什麼樣的影響,若是一個隊列的消費者因爲某些緣由消費消息的速度很慢,剛好發送端發送消息比較快,這樣就會致使消息在隊列中堆積。隊列中消息堆積到必定程度後會開啓分頁功能(這裏假設配置了分頁)。分頁操做對系統資源的損耗很大,存在大量的分頁操做可能會致使消息代理的健康檢測失敗,致使自檢測關閉服務。

    慢消費者檢測就是爲了解決這個問題,用戶能夠自定義一個消費速率閥值,當消費者的消費速率低於這個閥值時,會關閉消費者鏈接,代理刪除其相關訂閱隊列和隊列中全部消息來釋放寶貴的服務器資源。

    慢消費者檢測邏輯封裝在QueueImpl的內部類SlowConsumerReaperRunnable中。

1.2.4.2 地址

    在Artemis中地址表明了一個消息的端點,每一個地址都必須有全局惟一的名稱。一個地址能夠綁定多個隊列,而且能夠定義一個路由類型。

    Artemis中有兩類路由類型,路由類型決定了消息是如何路由到與地址關聯的隊列的:

Anycast(任播):以點對點的方式匹配地址中的單個隊列,一般若是須要負載均衡消費發送到某個地址的消息就用這種類型的地址;
Multicast(廣播):以發佈/訂閱的方式匹配地址中的每一個隊列。
    Artemis中地址相關的基本信息存儲在AddressInfo中,經過AddressControlImpl來獲取與地址相關一些信息。Binding是用來過濾地址和隊列間的綁定關係的。而地址和隊列間的消息路由則是封裝在PostOfficeImpl中。

    對於Binding的實現主要由兩類實現:LocalQueueBinding和RemoteQueueBindingImpl。LocalQueueBinding主要是針對同節點的綁定關係的路由,RemoteQueueBindingImpl負責集羣中不一樣節點間的綁定關係路由。

1.2.4.3 隊列

    隊列能夠認爲是一個消息的彙集地,全部待消費的消息都存儲在隊列中,隊列會管理並對消息進行投遞。Artemis爲隊列也定義了兩種路由類型,隊列的路由類型同地址路由路由類型分爲Anycast(任播)和Multicast(廣播)。隊列的Anycast(任播)路由類型表示多個消費者能夠以負載均衡的方式消費隊列中的消息;隊列的Multicast(廣播)路由類型表示消費者以訂閱的方式消費消息。

    Artemis中爲了提高消費的性能,隊列中的消息都存儲在內存中。同時爲了防止某個隊列佔用過多的內存,致使系統內存資源耗盡,Artemis支持爲每一個隊列定義隊列容許使用的最大內存。當隊列中堆積消息的大小超過設定的內存閥值,能夠觸發相應的機制來避免內存資源過量消耗,這是Artemis的一組自我保護機制。Artemis提供了四種策略來手段來處理此種狀況:

丟棄消息:當消息數量達到閥值時,Artemis會丟棄消息。客戶端無感知。
丟棄消息並向生產者拋出異常:當消息數量達到閥值時,Artemis會丟棄消息,而且向生產者客戶端拋出一個異常,客戶端可感知。
阻塞生產者:當消息數量達到閥值時,Artemis會向生產者發送一條特定消息,讓生產者本身阻塞發送,直到Artemis的隊列內存釋放後。這個功能須要客戶端的配合,某些協議的客戶端不支持這個功能;
消息分頁:當消息數量達到閥值時,會觸發分頁,將內存中的消息分頁到磁盤中。這個功能與客戶端使用的協議無關,全部協議均可支持,可是觸發分頁時會消耗磁盤IO並下降消費性能。
    以上策略各有優劣,用戶能夠根據本身實際狀況進行選擇。

    Artemis提供了兩個特殊隊列,這兩隊列都提供了一下普通隊列沒有的特殊功能,分別爲:

Last-value隊列:一個隊列被定義爲Last-value隊列時,任何具備相同屬性值的消息只會保留最新的消息,當隊列中接收到一個新的消息會去隊列中查找是否有遺留的且屬性相同的消息,若是有則拋棄遺留消息,將新消息放入隊列。Last-Value隊列在LastValueQueue中實現。
獨佔隊列:是指一個隊列中只容許有一個消費者。這是Artemis爲解決順序消費定義的特殊隊列。可是獨佔隊列沒法經過增長消費者數量來擴展消費能力。
    Artemis提供的Queue接口做爲隊列的抽象,有兩個實現類:

QueueImpl:是普通隊列和獨佔隊列共有的封裝;
LastValueQueue:則爲Last-value隊列的封裝
1.2.5 持久化層(Persistence)

   一個消息中間件的性能,消息持久化的性能優劣是最重要的一環。在計算機中,與CPU、內存、網絡效率相比,磁盤IO的效率是遠遠落後於他們。在應用程序性能優化的場景中,也常常會碰到數據持久化瓶頸,能夠說持久化優化是程序擴張之路上不得不解決的問題。

    Artemis提供了兩種持久方案,一種是針對消息高度優化的而且擁有出色性能的日誌存儲;另外一種是JDBC存儲,JDBC存儲在2.6.3版本以前還在開發階段,後續版本會繼續完善。下文將對日誌存儲作一個簡單介紹,因爲JDBC存儲還不完善就不作特別說明。

日誌存儲

    Artemis經過預建立好而且固定大小最初使用0填滿的日誌文件,對消息的任何增、刪、改操做都會記錄到日誌文件中。寫文件的方式爲在日誌文件末尾追加數據。同時使用日誌文件的大小是儘可能使文件大小恰好在一個磁盤柱面上,這樣爲了控制在使用文件最小化磁盤磁頭移動,由於磁頭移動一般是磁盤中最慢的操做。

    Artemis爲日誌存儲提供了三種實現,建議在操做系統支持的狀況下選擇AIO來與文件系統交互,達到最高性能。

Linux AIO:是基於Linux系統的異步IO庫(AIO)來實現與文件系統進行交互,Artemis提供了使用基於C語言封裝的調用Linux系統AIO的庫,在Artemis中要使用AIO要預先安裝好libaio而且運行在Linux 2.6以上的內核版本系統中。使用AIO一般會獲得比使用NIO更好的性能,基於AIO的消息持久化日誌系統能夠達到類比其餘消息中間中使用內存存儲消息的性能。
Java NIO:使用標準的Java NIO與文件系統進行交互,它提供了很是好的性能。在系統不支持AIO狀況下會自動使用NIO做爲與文件系統交互的方案。
內存映射:支持文件READ_WRITE的內存映射經過系統頁面緩存來與文件系統進行交互。這提供了與NIO至關的性能,而且在持久化過程當中不會產生堆垃圾,極大的減小了Java垃圾收集的頻率與時長。
 

 

2.Artemis高可用
    Artemis支持當一個或多個服務節點故障後具有將故障服務器節點遷移到健康服務器節點的能力,服務的故障遷移能力是中間件的高可用性的必備條件。單點故障不影響總體程序的服務,也是衡量程序健壯性的一個重要標誌。在實際場景中各種後臺服務程序都須要考慮高可用,只不過因爲各自服務的場景不一樣所選擇的方案有所不一樣。

    Artemis提供了兩種不一樣的備份策略來爲故障轉移提供支持,分別爲:主從複製(replication)和共享存儲(shared-store)。還有一種默認的策略叫作live-only,這個策略不會提升備份功能,只有單主節點存活。在使用備份時須要注意一個狀況,不管是主從複製仍是共享存儲,它們的備份只對持久化消息有效,對應非持久化消息是沒法使用備份功能的。

2.1 主從複製
    使用主從複製,主服務器和備份服務器不共享相同的數據目錄,全部數據同步都經過網絡完成。主服務器收到的消息都會複製到備份服務器。當備份服務器啓動時,備份服務器要先全量同步主服務器的數據,而且這個過程雖然不會對當前鏈接在主服務器的客戶端形成阻塞,可是會存在一個時刻備份服務器要確保徹底同步了主服務器的數據,這個時刻會阻止全部與持久化相關的操做。而且在數據同步的過程當中,備份服務器是徹底不可操做的。

     

     當主服務器故障時,備份服務器將接替主服務器對應提供服務。在主備切換過程當中,對因而否要進行切換,爲了不腦裂問題,Artemis經過選舉策略來儘量規避腦裂的發生。只有當集羣中絕大部分主服務器認爲能夠進行主備切換時,備服務器才能升級爲主服務器對外提供服務,在此以前備份服務器是沒法對外提供服務的。

2.2 共享存儲
     Artemis的共享存儲須要一個備份服務器和主服務器都能訪問到的共享文件系統。共享存儲的優勢是主節點和備份節點之間不須要複製同步數據,意味着不會因正常操做期間的複製開銷而受到任何性能損失。缺點是須要爲它們提供共額外的共享文件系統,而且其持久化性能取決於共享文件系統的性能。


原文地址

相關文章
相關標籤/搜索