9. RocketMQ Consumer-Startvorgang

Consumer-Startvorgang
Nachdem der Producer die Nachricht an den Broker gesendet hat, sorgt der Broker für die Persistenz. Anschließend können Sie einen Consumer erstellen, um die Nachricht zu konsumieren. Heute analysieren wir, wie wir beginnen Bei einem Consumer-Producer-Code ähnelt das Prinzip dem Starten des Producers.

Initialisierung der DefaultMQPushConsumer-Klasse

public DefaultMQPushConsumer(final String namespace, final String consumerGroup, RPCHook rpcHook,
    AllocateMessageQueueStrategy allocateMessageQueueStrategy) {
    
    
    // 消费者组
    this.consumerGroup = consumerGroup;
    // 命名空间
    this.namespace = namespace;
    // 分配策略
    this.allocateMessageQueueStrategy = allocateMessageQueueStrategy;
    // 创建DefaultMQPushConsumerImpl实例
    defaultMQPushConsumerImpl = new DefaultMQPushConsumerImpl(this, rpcHook);
}

DefaultMQPushConsumerImpl ähnelt als spezifische Implementierung von DefaultMQPushConsumer im Wesentlichen dem zuvor analysierten Producer. Diese Methode initialisiert und registriert die Hook-Funktion, registriert den MessageListener-Nachrichten-Listener und weist einige Parameter zu, z. B. namenevAddr.

abonnierenabonnieren

Wenn der Produzent erstellt wird, können Sie das zu sendende Thema angeben, und der Verbraucher muss nur die relevanten Themen abonnieren. Er kann auch Nachrichten basierend auf Tags und SQL filtern.


/**
 * 订阅topic支持消息过滤表达式
 * @param topic 订阅的topic
 * @param subExpression 只支持或(“||”)操作,如果是null或者“*”则表示订阅全部
 */
public void subscribe(String topic, String subExpression) throws MQClientException {
    
    
    this.defaultMQPushConsumerImpl.subscribe(withNamespace(topic), subExpression);
}
/**
 *
 */
public void  subscribe(String topic, String subExpression) throws MQClientException {
    
    
    try {
    
    
        // 解析订阅表达式生成SubscriptionData
        SubscriptionData subscriptionData = FilterAPI.buildSubscriptionData(topic, subExpression);
        // 将topic与SubscriptionData的关系维护到RebalanceImpl内部的subscriptionInner
        this.rebalanceImpl.getSubscriptionInner().put(topic, subscriptionData);
        if (this.mQClientFactory != null) {
    
    
            // 如果mQClientFactory不为null就发送心跳给所有broker
            this.mQClientFactory.sendHeartbeatToAllBrokerWithLock();
        }
    } catch (Exception e) {
    
    
        throw new MQClientException("subscription exception", e);
    }
}

Startmethode von DefaultMQPushConsumer

正式开始启动消费者,调用DefaultMQPushConsumer的start启动。
public void start() throws MQClientException {
    
    
  // 根据namespace和consumerGroup设置消费者组
    setConsumerGroup(NamespaceUtil.wrapNamespace(this.getNamespace(), this.consumerGroup));
    //  启动具体  
    this.defaultMQPushConsumerImpl.start();
  // 消费者轨迹追踪服务
    if (null != traceDispatcher) {
    
    
        try {
    
    
            traceDispatcher.start(this.getNamesrvAddr(), this.getAccessChannel());
        } catch (MQClientException e) {
    
    
            log.warn("trace dispatcher start failed ", e);
        }
    }
}

Die Startlogik von DefaultMQPushConsumer besteht hauptsächlich darin, Verbrauchergruppen basierend auf Namespace und ConsumerGroup einzurichten. Die Hauptimplementierung befindet sich immer noch in ihrer kurzsichtigen Implementierungsklasse DefaultMQPushConsumerImpl (Fassadenmodus).


public synchronized void start() throws MQClientException {
    
    
    // 根据服务状态实现不同的逻辑,只有当为CREATE_JUST才会正常启动,其他类型的都报错
    switch (this.serviceState) {
    
    
        case CREATE_JUST:
            log.info("the consumer [{}] start beginning. messageModel={}, isUnitMode={}", this.defaultMQPushConsumer.getConsumerGroup(),
                this.defaultMQPushConsumer.getMessageModel(), this.defaultMQPushConsumer.isUnitMode());
            this.serviceState = ServiceState.START_FAILED;
          // 检查配置
            this.checkConfig();
          // 拷贝订阅关系,为集群模式的消费者配置其对应的重试队列
            this.copySubscription();

            if (this.defaultMQPushConsumer.getMessageModel() == MessageModel.CLUSTERING) {
    
    
                this.defaultMQPushConsumer.changeInstanceNameToPID();
            }

            this.mQClientFactory = MQClientManager.getInstance().getOrCreateMQClientInstance(this.defaultMQPushConsumer, this.rpcHook);
            // 消费者组
            this.rebalanceImpl.setConsumerGroup(this.defaultMQPushConsumer.getConsumerGroup());
            // 消费者模式(广播/集群)
            this.rebalanceImpl.setMessageModel(this.defaultMQPushConsumer.getMessageModel());
            //Consumer负载均衡策略(集群模式才存在负载均衡)
            this.rebalanceImpl.setAllocateMessageQueueStrategy(this.defaultMQPushConsumer.getAllocateMessageQueueStrategy());
            this.rebalanceImpl.setmQClientFactory(this.mQClientFactory);

            this.pullAPIWrapper = new PullAPIWrapper(
                mQClientFactory,
                this.defaultMQPushConsumer.getConsumerGroup(), isUnitMode());
            this.pullAPIWrapper.registerFilterMessageHook(filterMessageHookList);

            if (this.defaultMQPushConsumer.getOffsetStore() != null) {
    
    
                this.offsetStore = this.defaultMQPushConsumer.getOffsetStore();
            } else {
    
    
                switch (this.defaultMQPushConsumer.getMessageModel()) {
    
    
                    // 广播模式(本地的Offset文件)
                    case BROADCASTING:
                        this.offsetStore = new LocalFileOffsetStore(this.mQClientFactory, this.defaultMQPushConsumer.getConsumerGroup());
                        break;
                    // 集群模式 (Broker里面的offset)
                    case CLUSTERING:
                        this.offsetStore = new RemoteBrokerOffsetStore(this.mQClientFactory, this.defaultMQPushConsumer.getConsumerGroup());
                        break;
                    default:
                        break;
                }
                this.defaultMQPushConsumer.setOffsetStore(this.offsetStore);
            }
            // 加载消息偏移量
            this.offsetStore.load();
            //Consumer中自行指定的回调函数。
            if (this.getMessageListenerInner() instanceof MessageListenerOrderly) {
    
    
                this.consumeOrderly = true;
                // MessageListenerOrderly服务
                this.consumeMessageService =
                    new ConsumeMessageOrderlyService(this, (MessageListenerOrderly) this.getMessageListenerInner());
            } else if (this.getMessageListenerInner() instanceof MessageListenerConcurrently) {
    
    
                this.consumeOrderly = false;
                // MessageListenerConcurrently服务
                this.consumeMessageService =
                    new ConsumeMessageConcurrentlyService(this, (MessageListenerConcurrently) this.getMessageListenerInner());
            }
          // 启动消息消费服务
            this.consumeMessageService.start();
            // 注册
            boolean registerOK = mQClientFactory.registerConsumer(this.defaultMQPushConsumer.getConsumerGroup(), this);
            if (!registerOK) {
    
    
                this.serviceState = ServiceState.CREATE_JUST;
                this.consumeMessageService.shutdown(defaultMQPushConsumer.getAwaitTerminationMillisWhenShutdown());
                throw new MQClientException("The consumer group[" + this.defaultMQPushConsumer.getConsumerGroup()
                    + "] has been created before, specify another name please." + FAQUrl.suggestTodo(FAQUrl.GROUP_NAME_DUPLICATE_URL),
                    null);
            }
          // 启动CreateMQClientInstance客户端通信实例
            // 包括netty服务,各种定时任务,拉取消息服务,rebalanceService服务。
            mQClientFactory.start();
            log.info("the consumer [{}] start OK.", this.defaultMQPushConsumer.getConsumerGroup());
            this.serviceState = ServiceState.RUNNING;
            break;
        case RUNNING:
        case START_FAILED:
        case SHUTDOWN_ALREADY:
            throw new MQClientException("The PushConsumer service state not OK, maybe started once, "
                + this.serviceState
                + FAQUrl.suggestTodo(FAQUrl.CLIENT_SERVICE_NOT_OK),
                null);
        default:
            break;
    }
    
    // 向NameServer拉取并更新当前消费者订阅的topic路由信息
    this.updateTopicSubscribeInfoWhenSubscriptionChanged();
    // 随机选择broker检查tags
    this.mQClientFactory.checkClientInBroker();
    // 发送心跳给所有Broker
    this.mQClientFactory.sendHeartbeatToAllBrokerWithLock();
    // 唤醒负载均衡服务rebalanceService,主动进行一次MessageQueue的重平衡。
    this.mQClientFactory.rebalanceImmediately();
}

Zugehörige Logikimplementierung
Konfiguration prüfen
Rufen Sie die checkConfig()-Methode auf, um eine Reihe von Parametern zu überprüfen.


private void checkConfig() throws MQClientException {
    
    
      // 检查消费组(不能为空,长度不能大于255,只能包含数字和字母)
        Validators.checkGroup(this.defaultMQPushConsumer.getConsumerGroup());
        if (null == this.defaultMQPushConsumer.getConsumerGroup()) {
    
    
            throw new MQClientException(
                "consumerGroup is null"
                    + FAQUrl.suggestTodo(FAQUrl.CLIENT_PARAMETER_CHECK_URL),
                null);
        }
      // 检查是否是默认值名,是则报错
        if (this.defaultMQPushConsumer.getConsumerGroup().equals(MixAll.DEFAULT_CONSUMER_GROUP)) {
    
    
            throw new MQClientException(
                "consumerGroup can not equal "
                    + MixAll.DEFAULT_CONSUMER_GROUP
                    + ", please specify another one."
                    + FAQUrl.suggestTodo(FAQUrl.CLIENT_PARAMETER_CHECK_URL),
                null);
        }
      // 检查消费模式(广播或集群)
        if (null == this.defaultMQPushConsumer.getMessageModel()) {
    
    
            throw new MQClientException(
                "messageModel is null"
                    + FAQUrl.suggestTodo(FAQUrl.CLIENT_PARAMETER_CHECK_URL),
                null);
        }
      //  ....
}

Überprüfen Sie zunächst, dass die Verbrauchergruppe nicht leer sein darf, die Länge nicht größer als 255 sein darf und nur Zahlen und Buchstaben enthalten darf. Gleichzeitig darf der Name der Verbrauchergruppe nicht mit dem Namen der Standardverbrauchergruppe identisch sein (DEFAULT_CONSUMER), andernfalls wird eine Ausnahme ausgelöst.

Die Überprüfung des Verbrauchsmodus kann nur im Cluster- oder Broadcast-Modus erfolgen, andernfalls wird eine Ausnahme ausgelöst.

Überprüfen Sie, ob ConsumeFromWhere leer ist. ConsumeFromWhere stellt die Verbrauchsstrategie dar. Ein einfaches Verständnis ist, wo die Nachricht konsumiert wird. Der Standardwert ist CONSUME_FROM_LAST_OFFSET (der letzte in der Warteschlange beginnt mit dem Konsum). Sie können auch CONSUME_FROM_FIRST_OFFSET (den ersten in der) angeben Warteschlange beginnt zu verbrauchen) und CONSUME_FROM_TIMESTAMP (geben Sie den Zeitstempel an). ).

Überprüfen Sie die Startzeit des Verbrauchs, die Ladestrategie allocateMessageQueueStrategy im Clustermodus, die Abonnementbeziehung, ob die Nachrichtenüberwachung registriert werden soll, die Anzahl lokaler Threads, das Zeitintervall für das Abrufen von Nachrichten, die Anzahl der gleichzeitig abgerufenen Nachrichten und die Anzahl der verbrauchten Nachrichten ein einziges Mal.

Abonnementbeziehung kopieren
besteht darin, das entsprechende Wiederholungsthema retryTopic für Verbraucher im Clustermodus zu konfigurieren und es zu binden, um eine Nachrichtenwiederholung zu implementieren, während Broadcast-Nachrichten keine Nachrichten senden.

private void copySubscription() throws MQClientException {
    
    
    try {
    
    
      // 订阅消息进行拷贝存入rebalanceImpl中
        Map<String, String> sub = this.defaultMQPushConsumer.getSubscription();
        if (sub != null) {
    
    
            for (final Map.Entry<String, String> entry : sub.entrySet()) {
    
    
                final String topic = entry.getKey();
                final String subString = entry.getValue();
                SubscriptionData subscriptionData = FilterAPI.buildSubscriptionData(topic, subString);
                this.rebalanceImpl.getSubscriptionInner().put(topic, subscriptionData);
            }
        }

        if (null == this.messageListenerInner) {
    
    
            this.messageListenerInner = this.defaultMQPushConsumer.getMessageListener();
        }
      // 消息类型,如果是集群消息进行绑定进行失败重试,如果是广播消息不做任何操作不重试直接丢弃
        switch (this.defaultMQPushConsumer.getMessageModel()) {
    
    
            case BROADCASTING:
                break;
            case CLUSTERING:
                final String retryTopic = MixAll.getRetryTopic(this.defaultMQPushConsumer.getConsumerGroup());
                SubscriptionData subscriptionData = FilterAPI.buildSubscriptionData(retryTopic, SubscriptionData.SUB_ALL);
                this.rebalanceImpl.getSubscriptionInner().put(retryTopic, subscriptionData);
                break;
            default:
                break;
        }
    } catch (Exception e) {
    
    
        throw new MQClientException("subscription exception", e);
    }
}

Hinweis: Clusternachrichten abonnieren automatisch das Wiederholungsthema des Verbrauchers. Wenn eine Nachricht fehlschlägt, wird sie erneut versucht, um einen Nachrichtenverlust zu verhindern. Broadcast-Nachrichten führen keine Vorgänge aus. Wenn eine Nachricht fehlschlägt, wird sie direkt verworfen. Die Nachricht geht verloren .

Andere Vorgänge
Rufen Sie die Methode getOrCreateMQClientInstance auf, um die Instanz CreateMQClientInstance abzurufen, legen Sie die relevanten Attribute des Lastausgleichsdienstes rebalanceImpl fest und erstellen Sie die Kern-Pull-Nachrichtenklasse PullAPIWrapper.

Legen Sie je nach Nachrichtenmodus unterschiedliche Speicherorte für die Offset-Datei fest, d. h. den Nachrichten-Offset. Im Broadcast-Modus wird die Offset-Datei lokal gespeichert. Im Cluster-Modus wird die Offset-Datei auf der Remote-Broker-Seite gespeichert und geladen gleichzeitig aufgerufen, um den Offset zu laden.

Geben Sie den angegebenen MessageListener an, um verschiedene ConsumeMessageService-Nachrichtendienste zu erstellen. Es gibt zwei Klassen: MessageListenerOrderly (sequentielle Nutzung) und MessageListenerConcurrently (gleichzeitige Nutzung).

Starten Sie den Verbraucherdienst this.consumeMessageService.start (), registrieren Sie den Dienst, starten Sie die Client-Kommunikationsinstanz CreateMQClientInstance, initialisieren Sie den Netty-Dienst, verschiedene geplante Aufgaben, Pull-Nachrichtendienste, RebalabceService-Dienste usw., ähnlich dem vorherigen Produzenten.

Supongo que te gusta

Origin blog.csdn.net/weixin_45817985/article/details/134952908
Recomendado
Clasificación