broker selection during message transmission rocketmq

DefaultMQProducerImpl file has a sendDefaultImpl, when sending a message from here to go, how to get the routing information will not expand here talk about it. 
In this method the inside, a synchronous mode, a message is not sent in accordance with the number of retries will continue to move
selectOneMessageQueue retry logic.
            for (; times < timesTotal; times++) {
                String lastBrokerName = null == mq ? null : mq.getBrokerName();
                MessageQueue mqSelected = this.selectOneMessageQueue(topicPublishInfo, lastBrokerName);
                if (mqSelected != null) {
                    mq = mqSelected;
                    brokersSent[times] = mq.getBrokerName();
                    try {
                        beginTimestampPrev = System.currentTimeMillis();
                        long costTime = beginTimestampPrev - beginTimestampFirst;
                        if (timeout < costTime) {
                            callTimeout = true;
                            break;
                        }

                        sendResult = this.sendKernelImpl(msg, mq, communicationMode, sendCallback, topicPublishInfo, timeout - costTime);
                        endTimestamp = System.currentTimeMillis();
                        this.updateFaultItem(mq.getBrokerName(), endTimestamp - beginTimestampPrev, false);
                        switch (communicationMode) {
                            case ASYNC:
                                return null;
                            case ONEWAY:
                                return null;
                            case SYNC:
                                if (sendResult.getSendStatus() != SendStatus.SEND_OK) {
                                    if (this.defaultMQProducer.isRetryAnotherBrokerWhenNotStoreOK()) {
                                        continue;
                                    }
                                }

                                return sendResult;
                            default:
                                break;
                        }
                    } catch (RemotingException e) {
                        endTimestamp = System.currentTimeMillis();
                        this.updateFaultItem(mq.getBrokerName(), endTimestamp - beginTimestampPrev, true);
                        log.warn(String.format("sendKernelImpl exception, resend at once, InvokeID: %s, RT: %sms, Broker: %s", invokeID, endTimestamp - beginTimestampPrev, mq), e);
                        log.warn(msg.toString());
                        exception = e;
                        continue;

 

selectOneMessageQueue here is actually called internally MQFaultStrategy internal objects selectOneMessageQueue:

 

My personal opinion, not that this estimation function is particularly important, so mq default is not to use this logic, but this does not prevent our research. Here is MQFaultStrategy of 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);
    }

  

If sendLatencyFaultEnable is false, default is false. Then remove all +1 each message queue Queue Number (it means the message queue of each broker unit has a queue, queue length specified by each broker configuration) inside the message, while the last failure weed out brokername.

 

There is a problem is if only two broker you can solve most problems, but if a lot of broker, then we have hope mq on a time dimension, a broker can be estimated when available. Especially for rocketmq, because when the broker changes, producer is not the first time be notified, but the resulting asynchronous in rotation. In addition also asynchronous polling explore living between nameserver with broker.

 

Open sendLatencyFaultEnable words, that is, before sending the message, estimate this broker is available, and if it is then returned directly available. The above code:

                        if (null == lastBrokerName || mq.getBrokerName().equals(lastBrokerName))

 

I feel it should be wrong, it should be mq.getBrokerName (). NotEquals (lastBrokerName)

 

There is a call latencyFaultTolerance.isAvailable to determine whether the broker is available, how this come about?

In fact, sendDefaultImpl time, regardless of whether the message is sent successfully or not, will be called internal producer MQFaultStrategy of updateFaultItem, will be here to update latencyFaultTolerance

Here are some important members of the MQFaultStrategy and important ways:

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


    public void updateFaultItem(final String brokerName, final long currentLatency, boolean isolation) {
        if (this.sendLatencyFaultEnable) {
            long duration = computeNotAvailableDuration(isolation ? 30000 : currentLatency);
            this.latencyFaultTolerance.updateFaultItem(brokerName, currentLatency, duration);
        }
    }

    private long computeNotAvailableDuration(final long currentLatency) {
        for (int i = latencyMax.length - 1; i >= 0; i--) {
            if (currentLatency >= latencyMax[i])
                return this.notAvailableDuration[i];
        }

        return 0;
    }

  

During sending the message sendDefaultImpl, only to send, this isolation is false, in general is 0, otherwise the greater the consumption of time this time to send a message to get through computeNotAvailableDuration duration, the greater the latencyMax get the serial number, take from notAvailableDuration to the greater duration.

If there is a fault, isolation is true, then consider this broker unavailable time is 180000L, is 3 minutes

 

Proceed to LatencyFaultToleranceImpl of updateFaultItem:

    @Override
    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);
        }
    }

  Here construct a faultitem, as the name suggests is wrong, there is a problem subjects, name is the broker-name, currentLatency is the last to send a message from the beginning to the end of time-consuming, the next time stamp estimate starttimestamp is available.

 

Continue to look at various important methods FaultItem:

      @Override
        public int compareTo(final FaultItem other) {
            if (this.isAvailable() != other.isAvailable()) {
                if (this.isAvailable())
                    return -1;

                if (other.isAvailable())
                    return 1;
            }

            if (this.currentLatency < other.currentLatency)
                return -1;
            else if (this.currentLatency > other.currentLatency) {
                return 1;
            }

            if (this.startTimestamp < other.startTimestamp)
                return -1;
            else if (this.startTimestamp > other.startTimestamp) {
                return 1;
            }

            return 0;
        }

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

  

Back to selectOneMessageQueue MQFaultStrategy strategy, combined with the above code, if it finds an available broker then return directly. If you can not find a similar return calls pickOneAtLeast find

public String pickOneAtLeast() {
        final Enumeration<FaultItem> elements = this.faultItemTable.elements();
        List<FaultItem> tmpList = new LinkedList<FaultItem>();
        while (elements.hasMoreElements()) {
            final FaultItem faultItem = elements.nextElement();
            tmpList.add(faultItem);
        }

        if (!tmpList.isEmpty()) {
            Collections.shuffle(tmpList);

            Collections.sort(tmpList);

            final int half = tmpList.size() / 2;
            if (half <= 0) {
                return tmpList.get(0).getName();
            } else {
                final int i = this.whichItemWorst.getAndIncrement() % half;
                return tmpList.get(i).getName();
            }
        }

        return null;
    }

  sorting according to quality has faultiitem support, then after sorted, good from the front half portion of a randomly selected re brokername

 

Guess you like

Origin www.cnblogs.com/notlate/p/11615878.html