ActiveMQ靜態網絡集羣

Networks of brokers

一、說明

是指多個broker組成集羣,當其中一個broker的消費者出問題導致消息堆積無法消費掉時,通過ActiveMQ支持的Network of Broker方案可將該broker堆積的消息轉發到其他有消費者的broker

ActiveMQ集羣有好幾種模式,其中網絡集羣模式分爲靜態網絡和動態網絡;本文主要介紹ActiveMQ的靜態網絡集羣模式;

1、靜態網絡:就是在Broker集羣配置中配置static的網絡地址,以供服務啓動的時候建立網絡集羣

靜態網絡的實現,主要是基於networkConnector元素配置完成

2、動態網絡:是通過配置發現地址,在服務啓動後還可以動態的增加節點,比較適用於動態擴容需求的場景;主要是基於transportConnector元素配置完成

二、集羣搭建

ActiveMQ的networkConnector默認是單向的,一個Broker在一端發送消息,另一個Broker在另一端接收消息,這就是所謂的"橋接"。ActiveMQ也支持雙向鏈接,創建一個雙向的通道對於兩個Broker不僅發送消息而且也能從相同的通道接收消息,通常作爲duplex connector來映射

一般推薦的靜態網絡應用架構如下:

架構圖片

以下根據筆者環境進行一個靜態網絡集羣的搭建;

(應爲硬件資源限制,筆者直接在單臺機器搭建一個集羣)

1. 目錄規劃

/opt/server

broker1、broker2、broker3

2. 服務器及端口規劃:

序號

服務名稱

服務器IP

服務端口

監控端口

1

broker1

10.10.1.44

61616

5762

1883

8161

2

broker2

10.10.1.44

61617

5763

1884

8162

3

broker3

10.10.1.44

61618

5764

1885

8163

爲了便於記憶,我在本地做了host映射,server1.com ,後在訪問服務器或是控制檯可以直接用http://server1.com:8161訪問

3. 服務器參數配置

以上三個節點需要在每個節點的 conf/activemq.xml中添加靜態網絡配置的參數,如下


<networkConnectors>

    <networkConnector name="bridge" uri="static://(tcp://localhost:61616,tcp://localhost:61617,tcp://localhost:61618)" dynamicOnly="false" duplex="true" />

</networkConnectors>

1.name: 默認的bridge

2.dynamicOnly: 默認是false,如果爲true,持久訂閱被激活時才創建對應的網絡持久訂閱。默認是啓動時激活

3.decreaseNetworkConsumerPriority: 默認是false。設定消費者優先權,如果爲true,網絡的消費者優先級降低爲-5。如果爲false,則默認跟本地消費者一樣爲0

4.networkTTL: 默認是1,網絡中用於消息和訂閱消費的broker數量

5.messageTTL: 默認是1,網絡中用於消息的broker數量

6.consumerTTL: 默認是1,網絡中用於消費的broker數量

7.conduitSubscriptions: 默認true,是否把同一個broker的多個consumer當做一個來處理(在做集羣的時候如果有多個consumer,需要設置爲false)

8.dynamicallyIncludedDestinations:默認爲空,要包括的動態消息地址,類適於excludedDestinations; 以下主要是參照示例

networkConnector配置的可用屬性

Broker1

Broker2

Broker2配置圖例

Broker3

Broker3配置圖例

完成以上配置然後重啓各個Broker

驗證是否配置成功

直接打開MQ的WEB控制檯,默認地址 http://10.10.1.44:8161/ 到network的菜單項可以看到如下效果

然後到另外兩個控制檯查看是否也有如下配置,如果都有顯示說明已經配置好,如果沒有說明哪裏配置的有問題,請參照上面步驟檢查。

 

4. 消息驗證

4.1、生產者客戶端發送消息到Broker1,Consumer客戶端從Broker3或Broker2上面訂閱消息

示例代碼: Producer

public class MQProducer {
   public static void main(String[] args) throws JMSException {
      // 連接到ActiveMQ服務器
      ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("tcp://server1.com:61616");
      Connection connection = factory.createConnection();
      connection.start();
      Session session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
      // 創建主題
      Topic topic = session.createTopic("VirtualTopic.TEST");
      MessageProducer producer = session.createProducer(topic);
      //  NON_PERSISTENT 非持久化 PERSISTENT 持久化,發送消息時用使用持久模式
      producer.setDeliveryMode(DeliveryMode.PERSISTENT);
      TextMessage message = session.createTextMessage();
      for (int i = 0; i < 4; i++) {
         message.setText("topic 消息。"+i);
         message.setStringProperty("property", "消息Property");
         //  發佈主題消息
         producer.send(message);
      }
      System.out.println("消息發送完畢!");
      session.close();
      connection.close();
   }

}

示例代碼:Consume

public class MQConsumer {
   public static void main(String[] args) throws JMSException {
      // 連接到ActiveMQ服務器
      ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("tcp://server1.com:61618");
      Connection connection = factory.createConnection();
      connection.start();
      Session session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
      // 創建主題
      Queue topicA = session.createQueue("Consumer.A.VirtualTopic.TEST");
      Queue topicB = session.createQueue("Consumer.B.VirtualTopic.TEST");
      // 創建消費者A組1-3號訂閱
      String port = "61618-";
      createConsumerQueue(port + "A1", topicA, session);
//    createConsumerQueue(port + "A2", topicA, session);
      // createConsumerQueue("A3", topicA, session);
      // 創建消費者B組1-3號訂閱
      // createConsumerQueue("B1", topicB, session);
      // createConsumerQueue("B2", topicB, session);
      // createConsumerQueue("B3", topicB, session);
   }

   private static void createConsumerQueue(String name, Queue topic, Session session) throws JMSException {
      // 創建消費者A組訂閱
      MessageConsumer consumerA1 = session.createConsumer(topic);
      consumerA1.setMessageListener(new MessageListener() {
         @Override
         public void onMessage(Message msg) {
            try {
               System.out.println(msg.getJMSType());
               if (msg instanceof TextMessage) {
                  TextMessage tm = (TextMessage) msg;
                  System.out.println("Received message " + name + ": " + tm.getText() + ":"
                        + tm.getStringProperty("property"));

               } else if (msg instanceof ActiveMQBytesMessage) {
                  ActiveMQBytesMessage bytesMessage = (ActiveMQBytesMessage) msg;
                  if (bytesMessage != null) {
                     byte[] bt = new byte[(int) bytesMessage.getBodyLength()];
                     bytesMessage.readBytes(bt);
                     System.out.println("Received message " + name + ": " +new String(bt));
                  }
               }
            } catch (JMSException e1) {
               e1.printStackTrace();
            }
         }
      });
   }
}

以上,先運行Consumer 61618 和61617,再運行Producer;可以看到消息被兩個客戶端均勻消費

61617

61618

通過以上可以發現,集羣搭建已經生效

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章