RocketMQ:高可用性實施

在優銳課的java學習分享中,我們探索了RocketMQ的HA實現。我們可以看到,碼了很多專業的相關知識, 分享給大家參考學習。

介紹

當我們談論HA時,通常人們會想到故障轉移機制。 但是,使羣集可用於消息也被認爲是HA。 在某種程度上,我認爲這比僅提供經紀人更重要。 畢竟,用戶可以並且將會感受到這種可用性的影響。

代碼段

這是場景:
假設集羣中有2個代理:master-a和master-b。 每個都有四個隊列:master-a(q0,q1,q2,q3)和master-b(q0,q1,q2,q3)。 最後一條消息已發送給master-a q0。 現在,master-a退出了。

這裏的目標是盡最大努力繼續傳遞消息。
有兩種可能性:

如果未檢測到中斷

在這種情況下,RocketMQ將重試3次:

org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl#sendDefaultImpl:
int timesTotal = communicationMode == CommunicationMode.SYNC ? 1 + this.defaultMQProducer.getRetryTimesWhenSendFailed() : 1;
for (; times < timesTotal; times++) {
    // ...
}

默認數字爲三,並且是可配置的。 有三種發送機制:SYNC,ASYNC和ONEWAY。

如果已檢測到中斷

好。 現在的問題變成了:我們如何避免去掌握-a?
首先,我們需要設置sendLatencyFaultEnable = true,然後選擇一個消息隊列:

org.apache.rocketmq.client.impl.producer.TopicPublishInfo#selectOneMessageQueue:
public MessageQueue selectOneMessageQueue(final TopicPublishInfo tpInfo, final String lastBrokerName) {
   if (this.sendLatencyFaultEnable) {
     try {
      int index = tpInfo.getSendWhichQueue().getAndIncrement();
      for (int i = 0; i < tpInfo.getMessageQueueList().size(); i++) {
        int pos = Math.abs(index++) % tpInfo.getMessageQueueList().size();
        if (pos < 0)
          pos = 0;
        MessageQueue mq = tpInfo.getMessageQueueList().get(pos);
        if (latencyFaultTolerance.isAvailable(mq.getBrokerName())) {
          if (null == lastBrokerName || mq.getBrokerName().equals(lastBrokerName))
            return mq;
        }
      }
      final String notBestBroker = latencyFaultTolerance.pickOneAtLeast();
      int writeQueueNums = tpInfo.getQueueIdByBroker(notBestBroker);
      if (writeQueueNums > 0) {
        final MessageQueue mq = tpInfo.selectOneMessageQueue();
        if (notBestBroker != null) {
          mq.setBrokerName(notBestBroker);
          mq.setQueueId(tpInfo.getSendWhichQueue().getAndIncrement() % writeQueueNums);
        }
        return mq;
      } else {
        latencyFaultTolerance.remove(notBestBroker);
      }
    } catch (Exception e) {
      log.error("Error occurred when selecting message queue", e);
    }
    return tpInfo.selectOneMessageQueue();
  }
  return tpInfo.selectOneMessageQueue(lastBrokerName);
}

重要的部分是:
delayFaultTolerance.isAvailable(mq.getBrokerName()); (第13行)
在正常時間,發送消息時,它將調用updateFaultItem():

org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl#sendDefaultImpl
sendResult = this.sendKernelImpl(msg, mq, communicationMode, sendCallback, topicPublishInfo, timeout - costTime);
endTimestamp = System.currentTimeMillis();
this.updateFaultItem(mq.getBrokerName(), endTimestamp - beginTimestampPrev, false);

當發生中斷時,將調用相同的方法:

endTimestamp = System.currentTimeMillis();
this.updateFaultItem(mq.getBrokerName(), endTimestamp - beginTimestampPrev, true);

incurrentLatency表示當前延遲,隔離是是否避免使用此代理的標誌。即,傳輸成功後,請勿避免使用此代理。 否則請避免。

delaymax和notAvailableDuration算法中的兩個核心變量。 如果發生故障,他們還決定startTimestamp的值。

從代碼中,我們可以看到經紀人是否缺席,等待時間爲30秒。 然後,我們將從下往上掃描latencyMax'' 直到找到一個小於currentLatency的數字。這是latencyMax’'和notAvailableDuration的默認值:

private long[] latencyMax = {50L, 100L, 550L, 1000L, 2000L, 3000L, 15000L};
private long[] notAvailableDuration = {0L, 0L, 30000L, 60000L, 120000L, 180000L, 600000L};

如果Isolation = true,則此代理將避免10分鐘。 否則,它取決於消息的延遲。

FalutItem存儲失敗的代理的信息,包括其名稱,延遲和避免開始的時間。

org.apache.rocketmq.client.latency.LatencyFaultToleranceImpl#updateFaultItem
public void updateFaultItem(final String name, final long currentLatency, final long notAvailableDuration) {
  FaultItem old = this.faultItemTable.get(name);
  if (null == old) {
    final FaultItem faultItem = new FaultItem(name);
    faultItem.setCurrentLatency(currentLatency);
    faultItem.setStartTimestamp(System.currentTimeMillis() + notAvailableDuration);
    old = this.faultItemTable.putIfAbsent(name, faultItem);
    if (old != null) {
      old.setCurrentLatency(currentLatency);
      old.setStartTimestamp(System.currentTimeMillis() + notAvailableDuration);
    }
  } else {
    old.setCurrentLatency(currentLatency);
    old.setStartTimestamp(System.currentTimeMillis() + notAvailableDuration);
  }
}

此類將更新失敗的代理的狀態。 並且還將決定是否恢復了代理並重新加入集羣。 這是方法:

org.apache.rocketmq.client.latency.LatencyFaultToleranceImpl.FaultItem#isAvailable:

public boolean isAvailable() {
  return (System.currentTimeMillis() - startTimestamp) >= 0;
}

結論

到目前爲止,我們已經檢查了用於實現HA機制的主要代碼塊。 基本上,將盡最大努力來傳遞消息。 我們看到許多持續的輪詢和重試。 這確實是企業級解決方案的現實。 通過顯示此代碼,我們想證明在確保集羣的高可用性方面所做的努力。

喜歡這篇文章的可以點個贊,歡迎大家留言評論,記得關注我,每天持續更新技術乾貨、職場趣事、海量面試資料等等
如果你對java技術很感興趣也可以加入我的java學習羣 V–(ddmsiqi)來交流學習,裏面都是同行,驗證【CSDN2】有資源共享。
不要再用"沒有時間“來掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來的自己一個交代
在這裏插入圖片描述

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