[mq] Réalisez l'envoi et la réception par lots de messages mq-12 à partir de zéro

Examen des prospects

[mq] Implémenter mq-01-producteur et démarrage consommateur à partir de zéro

[mq] Pour implémenter mq-02 à partir de zéro - comment implémenter un producteur appelant un consommateur ?

[mq] Mise en œuvre de mq-03-introduction d'intermédiaires de courtage à partir de zéro

[mq] Réalisez la détection et l'optimisation mq-04-start à partir de zéro

[mq] Implémenter mq-05 à partir de zéro - obtenir un arrêt en douceur

[mq] Réalisez le rythme cardiaque de détection du rythme cardiaque mq-06-consumer à partir de zéro

[mq] Réaliser l'équilibrage de charge mq-07-load balance à partir de zéro

[mq] Implémenter l'optimisation de la configuration mq-08 en partant de zéro

[mq] Implémenter mq-09-consumer pull message pull message from scratch

[mq] Mettre en œuvre mq-10-consumer pull message reception pull message ack from scratch

[mq] Implémenter la réception du message mq-11-consumer à partir de zéro ajouter des informations de groupe tirer le message ack groupName

[mq] Réalisez l'envoi et la réception par lots de messages mq-12 à partir de zéro

Message en masse

Pour l'envoi de messages, il peut parfois être nécessaire d'en envoyer plusieurs à la fois, comme les messages de journalisation.

Les opérations en bloc peuvent améliorer les performances.

Dans cette section, le vieux cheval ajoutera une petite fonctionnalité de lot avec vous.

lot

Envoi de messages en masse

Mise en œuvre du producteur

Définition des interfaces

/**
 * 同步发送消息-批量
 * @param mqMessageList 消息类型
 * @return 结果
 * @since 0.1.3
 */
SendBatchResult sendBatch(final List<MqMessage> mqMessageList);

/**
 * 单向发送消息-批量
 * @param mqMessageList 消息类型
 * @return 结果
 * @since 0.1.3
 */
SendBatchResult sendOneWayBatch(final List<MqMessage> mqMessageList);
复制代码

Prend en charge l'envoi de plusieurs messages à la fois.

implémentation d'interfaces

Le producteur est implémenté comme suit.

@Override
public SendBatchResult sendBatch(List<MqMessage> mqMessageList) {
    final List<String> messageIdList = this.fillMessageList(mqMessageList);
    final MqMessageBatchReq batchReq = new MqMessageBatchReq();
    batchReq.setMqMessageList(mqMessageList);
    String traceId = IdHelper.uuid32();
    batchReq.setTraceId(traceId);
    batchReq.setMethodType(MethodType.P_SEND_MSG_BATCH);
    return Retryer.<SendBatchResult>newInstance()
            .maxAttempt(maxAttempt)
            .callable(new Callable<SendBatchResult>() {
                @Override
                public SendBatchResult call() throws Exception {
                    return doSendBatch(messageIdList, batchReq, false);
                }
            }).retryCall();
}

@Override
public SendBatchResult sendOneWayBatch(List<MqMessage> mqMessageList) {
    List<String> messageIdList = this.fillMessageList(mqMessageList);
    MqMessageBatchReq batchReq = new MqMessageBatchReq();
    batchReq.setMqMessageList(mqMessageList);
    String traceId = IdHelper.uuid32();
    batchReq.setTraceId(traceId);
    batchReq.setMethodType(MethodType.P_SEND_MSG_ONE_WAY_BATCH);
    return doSendBatch(messageIdList, batchReq, true);
}


private SendBatchResult doSendBatch(List<String> messageIdList,
                               MqMessageBatchReq batchReq,
                               boolean oneWay) {
    log.info("[Producer] 批量发送消息 messageIdList: {}, batchReq: {}, oneWay: {}",
            messageIdList, JSON.toJSON(batchReq), oneWay);
    // 以第一个 sharding-key 为准。
    // 后续的会被忽略
    MqMessage mqMessage = batchReq.getMqMessageList().get(0);
    Channel channel = getChannel(mqMessage.getShardingKey());
    //one-way
    if(oneWay) {
        log.warn("[Producer] ONE-WAY send, ignore result");
        return SendBatchResult.of(messageIdList, SendStatus.SUCCESS);
    }
    MqCommonResp resp = callServer(channel, batchReq, MqCommonResp.class);
    if(MqCommonRespCode.SUCCESS.getCode().equals(resp.getRespCode())) {
        return SendBatchResult.of(messageIdList, SendStatus.SUCCESS);
    }
    throw new MqException(ProducerRespCode.MSG_SEND_FAILED);
}
复制代码

ps : Il y a une différence entre ceci et un envoi simple, et c'est le choix du canal. Comme une seule peut être sélectionnée, la clé de partitionnement de chaque message ne peut pas être prise en compte.

Manipulation des courtiers

distribution de messages

// 生产者消息发送-批量
if(MethodType.P_SEND_MSG_BATCH.equals(methodType)) {
    return handleProducerSendMsgBatch(channelId, json);
}

// 生产者消息发送-ONE WAY-批量
if(MethodType.P_SEND_MSG_ONE_WAY_BATCH.equals(methodType)) {
    handleProducerSendMsgBatch(channelId, json);
    return null;
}
复制代码

Mise en œuvre

/**
 * 处理生产者发送的消息
 *
 * @param channelId 通道标识
 * @param json 消息体
 * @since 0.1.3
 */
private MqCommonResp handleProducerSendMsgBatch(String channelId, String json) {
    MqMessageBatchReq batchReq = JSON.parseObject(json, MqMessageBatchReq.class);
    final ServiceEntry serviceEntry = registerProducerService.getServiceEntry(channelId);
    List<MqMessagePersistPut> putList = buildPersistPutList(batchReq, serviceEntry);

    MqCommonResp commonResp = mqBrokerPersist.putBatch(putList);

    // 遍历异步推送
    for(MqMessagePersistPut persistPut : putList) {
        this.asyncHandleMessage(persistPut);
    }
    return commonResp;
}
复制代码

Ici, la liste des messages est conservée.

La stratégie de persistance démontrée est la suivante :

@Override
public MqCommonResp putBatch(List<MqMessagePersistPut> putList) {
    // 构建列表
    for(MqMessagePersistPut put : putList) {
        this.doPut(put);
    }

    MqCommonResp commonResp = new MqCommonResp();
    commonResp.setRespCode(MqCommonRespCode.SUCCESS.getCode());
    commonResp.setRespMessage(MqCommonRespCode.SUCCESS.getMsg());
    return commonResp;
}
复制代码

Accusé de réception en masse pour les messages

illustrer

L'implémentation précédente consiste à effectuer un ACK après chaque consommation de message.

Pour la consommation de messages de la stratégie pull, nous pouvons attendre la fin du lot en cours et émettre un reçu ACK de manière uniforme.

Réalisation de la consommation

L'ajustement est mis en œuvre comme suit :

for(MqTopicTagDto tagDto : subscribeList) {
    final String topicName = tagDto.getTopicName();
    final String tagRegex = tagDto.getTagRegex();
    MqConsumerPullResp resp = consumerBrokerService.pull(topicName, tagRegex, size);

    if(MqCommonRespCode.SUCCESS.getCode().equals(resp.getRespCode())) {
        List<MqMessage> mqMessageList = resp.getList();
        if(CollectionUtil.isNotEmpty(mqMessageList)) {
            List<MqConsumerUpdateStatusDto> statusDtoList = new ArrayList<>(mqMessageList.size());
            for(MqMessage mqMessage : mqMessageList) {
                IMqConsumerListenerContext context = new MqConsumerListenerContext();
                final String messageId = mqMessage.getTraceId();
                ConsumerStatus consumerStatus = mqListenerService.consumer(mqMessage, context);
                log.info("消息:{} 消费结果 {}", messageId, consumerStatus);

                // 状态同步更新
                if(!ackBatchFlag) {
                    MqCommonResp ackResp = consumerBrokerService.consumerStatusAck(messageId, consumerStatus);
                    log.info("消息:{} 状态回执结果 {}", messageId, JSON.toJSON(ackResp));
                } else {
                    // 批量
                    MqConsumerUpdateStatusDto statusDto = new MqConsumerUpdateStatusDto();
                    statusDto.setMessageId(messageId);
                    statusDto.setMessageStatus(consumerStatus.getCode());
                    statusDto.setConsumerGroupName(groupName);
                    statusDtoList.add(statusDto);
                }
            }

            // 批量执行
            if(ackBatchFlag) {
                MqCommonResp ackResp = consumerBrokerService.consumerStatusAckBatch(statusDtoList);
                log.info("消息:{} 状态批量回执结果 {}", statusDtoList, JSON.toJSON(ackResp));
                statusDtoList = null;
            }
        }
    } else {
        log.error("拉取消息失败: {}", JSON.toJSON(resp));
    }
}
复制代码

Si ackBatchFlag = false, la logique de traitement est la même qu'avant.

Si ackBatchFlag = true, placez le message dans la liste en premier et exécutez-le uniformément après la fin.

implémentation du courtier

distribution de messages

//消费者消费状态 ACK-批量
if(MethodType.C_CONSUMER_STATUS_BATCH.equals(methodType)) {
    MqConsumerUpdateStatusBatchReq req = JSON.parseObject(json, MqConsumerUpdateStatusBatchReq.class);
    final List<MqConsumerUpdateStatusDto> statusDtoList = req.getStatusList();
    return mqBrokerPersist.updateStatusBatch(statusDtoList);
}
复制代码

accomplir

L'implémentation de la persistance par défaut, mise à jour comme suit :

@Override
public MqCommonResp updateStatusBatch(List<MqConsumerUpdateStatusDto> statusDtoList) {
    for(MqConsumerUpdateStatusDto statusDto : statusDtoList) {
        this.doUpdateStatus(statusDto.getMessageId(), statusDto.getConsumerGroupName(),
                statusDto.getMessageStatus());
    }

    MqCommonResp commonResp = new MqCommonResp();
    commonResp.setRespCode(MqCommonRespCode.SUCCESS.getCode());
    commonResp.setRespMessage(MqCommonRespCode.SUCCESS.getMsg());
    return commonResp;
}
复制代码

Parcourez chaque élément et mettez à jour l'état.

résumé

L'asynchrone et le traitement par lots sont les deux moyens les plus courants d'améliorer les performances.

La mise en œuvre par lots est la plus simple et la plus efficace.

J'espère que cet article vous sera utile. Si vous l'aimez, veuillez l'aimer, le collecter et le transférer.

Je suis un vieux cheval et j'ai hâte de vous revoir la prochaine fois.

adresse open source

La file d'attente de messages en java. (implémentation Java version simple mq) github.com/houbb/mq

Lecture étendue

rpc - Implémentation de rpc à partir de zéro github.com/houbb/rpc

Je suppose que tu aimes

Origine juejin.im/post/7098894399630737415
conseillé
Classement