RocketMQメッセージの負荷分散戦略分析-図、ソースレベルの分析

一緒に書く習慣をつけましょう!「ナゲッツデイリーニュープラン・4月アップデートチャレンジ」に参加して10日目です。クリックしてイベントの詳細をご覧ください


プロデューサーがメッセージを送信すると、最初にトピックルーティング情報を取得します(ローカル+登録センターを介してプルされます)。RocketMQアーキテクチャには複数のブローカーサーバーがあり、メッセージキューも複数のブローカーサーバーに存在するため、負荷がかかります。バランシング戦略が必要です。すべてのサーバーにトラフィックを可能な限り均等に分散します。

ここに画像の説明を挿入

この章では、RocketMQで一般的に使用される4つの負荷分散戦略を紹介します。



デフォルトポリシー

プロデューサーがメッセージを送信するときにメッセージキューを選択するロジックを見つけ、クラスでメソッドDefaultMQProducerImplを定義します。メソッドを入力します。sendDefaultImplここに画像の説明を挿入selectOneMessageQueue

public MessageQueue selectOneMessageQueue(final TopicPublishInfo tpInfo, final String lastBrokerName) {
        return this.mqFaultStrategy.selectOneMessageQueue(tpInfo, lastBrokerName);
    }
复制代码

上記のコードにはMQFaultStrategy、クラスで定義されたメソッドがありselectOneMessageQueueます。

public class MQFaultStrategy {
	/**
     * 默认负载均衡策略
     * 
     * @param tpInfo
     * @param lastBrokerName
     * @return
     */
    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);
                    // 选取时仍然会先选择相同集群下的其他MessageQueue
                    if (latencyFaultTolerance.isAvailable(mq.getBrokerName())) {
                        if (null == lastBrokerName || mq.getBrokerName().equals(lastBrokerName))
                            return mq;
                    }
                }
				
				// 从其他Broker里选择一个,该列表的节点根据是否可用,超时时间和最新可用时间做了排序
                /*
                 * ......
                 */
                
            } catch (Exception e) {
            }
            // 默认策略
            return tpInfo.selectOneMessageQueue();
        }
        // 延迟容忍开关没开时的默认策略
        return tpInfo.selectOneMessageQueue(lastBrokerName);
    }
}
复制代码

ソースコードによると、デフォルトの戦略は、送信するメッセージキューを順番に選択することであることがはっきりとわかります。具体的な実行の詳細は次のとおりです。

  1. 遅延フォールトトレランススイッチがオンになっているかどうかを判断します。オンになっている場合は、デフォルトの戦略に従ってMQを返します。それ以外の場合は、使用中TopicPublishInfoselectOneMessageQueue(lastBrokerName)メソッドを使用してMQを直接返します。
  2. 現在ポーリングされているMQのインデックスを取得します。メッセージが初めて送信されるときに、メッセージにThreadLocal格納されている値が空の場合はランダムに数値が生成され、それ以外の場合はこの数値に1が加算されます。

tpInfo.getSendWhichQueue()ここにありますThreadLocal。関連情報については、javaguide.cn / java/concur…を参照してください。

  1. クラスターの最後の送信に失敗した場合、同じクラスター内の他のメッセージキューが最初に選択されます。
  2. 手順3で選択されていない場合は、以前に失敗したリストからより適切なブローカーを選択します

より良いブローカーを選ぶ方法は?RocketMQの実装は、可用性、タイムアウト、および利用可能な最新の時間に従ってリスト内のノードを並べ替えることです。

  1. 手順3と4が選択されていない場合は、デフォルトの戦略に進みます(新しいMQをポーリングします)。
public MessageQueue selectOneMessageQueue() {
        int index = this.sendWhichQueue.getAndIncrement();
        int pos = Math.abs(index) % this.messageQueueList.size();
        if (pos < 0)
            pos = 0;
        return this.messageQueueList.get(pos);
    }
复制代码
  1. 最初のステップで遅延フォールトトレランススイッチがオンになっていない場合は、次のように入力します。
public MessageQueue selectOneMessageQueue(final String lastBrokerName) {
        if (lastBrokerName == null) {
            return selectOneMessageQueue();
        } else {
            int index = this.sendWhichQueue.getAndIncrement();
            for (int i = 0; i < this.messageQueueList.size(); i++) {
                int pos = Math.abs(index++) % this.messageQueueList.size();
                if (pos < 0)
                    pos = 0;
                MessageQueue mq = this.messageQueueList.get(pos);
                if (!mq.getBrokerName().equals(lastBrokerName)) {
                    return mq;
                }
            }
            return selectOneMessageQueue();
        }
    }
复制代码

最後に使用されなかったMQをブローカーから選択します。


上記のプロセスは、フローチャートで次のように要約されます。 ここに画像の説明を挿入



ランダム戦略

使い方

プログラミングでは、ランダムな戦略を使用したい場合、それは非常に簡単で、セレクターを渡すだけです。

producer.send(message, new SelectMessageQueueByRandoom(), " ");
复制代码

さらに興味深い問題があります。ここではバージョン3.5.8のRocketMQを使用しています。上記のメソッドの[random]という単語のつづりが間違っています。正しいはずです。Random最初は間違いかもしれません。後で、互換性のために名前を直接変更するのは良くありません。

ソースコード

public class SelectMessageQueueByRandoom implements MessageQueueSelector {
    private Random random = new Random(System.currentTimeMillis());


    @Override
    public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
        int value = random.nextInt();
        if (value < 0) {
            value = Math.abs(value);
        }

        value = value % mqs.size();
        return mqs.get(value);
    }
}
复制代码

SelectMessageQueueByRandoomソースコードも非常に読みやすいです。つまり、MQをランダムに選択して戻ります。



ハッシュ戦略

使い方

ハッシュ戦略を使用してメッセージを送信するには、SelectMessageQueueByHashオブジェクトを渡すだけです。

producer.send(message, new SelectMessageQueueByHash(), " ");
复制代码

ソースコード

public class SelectMessageQueueByHash implements MessageQueueSelector {

    @Override
    public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
    	// arg的计算哈希值
        int value = arg.hashCode();
        if (value < 0) {
            value = Math.abs(value);
        }

        value = value % mqs.size();
        return mqs.get(value);
    }
}
复制代码

ランダム戦略と同様に、ハッシュ負荷分散戦略も非常に単純です。どのMQを返すかは、argのハッシュ値によって決まります。



近接戦略

使い方

ハッシュ戦略を使用してメッセージを送信するには、SelectMessageQueueByMachineRoomオブジェクトを渡すだけです。

producer.send(message, new SelectMessageQueueByMachineRoom(), " ");
复制代码

ソースコード

public class SelectMessageQueueByMachineRoom implements MessageQueueSelector {
    private Set<String> consumeridcs;


    @Override
    public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
        return null;
    }


    public Set<String> getConsumeridcs() {
        return consumeridcs;
    }


    public void setConsumeridcs(Set<String> consumeridcs) {
        this.consumeridcs = consumeridcs;
    }
}
复制代码

興味深いのは、コンピュータールーム戦略の選択コードがRocketMQで記述されておらず、nullを直接返すことです。ユーザーがこの要件を満たしている場合は、自分で記述する必要があります。

おすすめ

転載: juejin.im/post/7084825497393168414