随笔 - 295  文章 - 0  评论 - 16  阅读 - 41万 

同步发送消息的失败重试
同步模式下,最多发送 3 次,当前轮次发送失败,下次选择另外一个 broker(如果存在)发送
如果设置了 sendLatencyFaultEnable 参数,会把当前的 broker 隔离一段时间

发送失败的情形
1. 消息未发出

复制代码
// 消息未发出,通过 ChannelFuture 的状态得知
channel.writeAndFlush(request).addListener(new ChannelFutureListener() {
    @Override
    public void operationComplete(ChannelFuture f) throws Exception {
        if (f.isSuccess()) {
            responseFuture.setSendRequestOK(true);
            return;
        } else {
            responseFuture.setSendRequestOK(false);
        }

        responseTable.remove(opaque);
        responseFuture.setCause(f.cause());
        responseFuture.putResponse(null);
        log.warn("send a request command to channel <" + addr + "> failed.");
    }
});
复制代码

2. 消息发出,等待响应超时

RemotingCommand responseCommand = responseFuture.waitResponse(timeoutMillis);

3. 消息发出,响应错误

 

当消息发送成功,等待 broker 的响应时发生超时,客户端有理由认为是网络不好,数据没有到达 broker,因此重复发送消息,也就是这种情况会导致 broker 存在重复消息。当发生 RemotingException 或 MQClientException 时,会重复发送。

复制代码
// org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl#sendDefaultImpl
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();
            if (times > 0) {
                //Reset topic with namespace during resend.
                msg.setTopic(this.defaultMQProducer.withNamespace(msg.getTopic()));
            }
            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;
        } catch (MQClientException 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;
        } catch (MQBrokerException 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;
            switch (e.getResponseCode()) {
                case ResponseCode.TOPIC_NOT_EXIST:
                case ResponseCode.SERVICE_NOT_AVAILABLE:
                case ResponseCode.SYSTEM_ERROR:
                case ResponseCode.NO_PERMISSION:
                case ResponseCode.NO_BUYER_ID:
                case ResponseCode.NOT_IN_CURRENT_UNIT:
                    continue;
                default:
                    if (sendResult != null) {
                        return sendResult;
                    }

                    throw e;
            }
        } catch (InterruptedException e) {
            endTimestamp = System.currentTimeMillis();
            this.updateFaultItem(mq.getBrokerName(), endTimestamp - beginTimestampPrev, false);
            log.warn(String.format("sendKernelImpl exception, throw exception, InvokeID: %s, RT: %sms, Broker: %s", invokeID, endTimestamp - beginTimestampPrev, mq), e);
            log.warn(msg.toString());

            log.warn("sendKernelImpl exception", e);
            log.warn(msg.toString());
            throw e;
        }
    } else {
        break;
    }
}
复制代码

 

posted on   偶尔发呆  阅读(797)  评论(0编辑  收藏  举报
编辑推荐:
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
· .NET 适配 HarmonyOS 进展
阅读排行:
· DeepSeek+PageAssist实现本地大模型联网
· 手把手教你更优雅的享受 DeepSeek
· 腾讯元宝接入 DeepSeek R1 模型,支持深度思考 + 联网搜索,好用不卡机!
· 从 14 秒到 1 秒:MySQL DDL 性能优化实战
· AI工具推荐:领先的开源 AI 代码助手——Continue
历史上的今天:
2019-01-17 kafka 心跳和 rebalance
2018-01-17 dubbo为consumer创建代理
2018-01-17 线程同步知识点
点击右上角即可分享
微信分享提示