记一次pulsar数据丢失排查

本文描述了一次在使用Pulsar时遇到的数据丢失问题,通过排查发现是由于生产者端内存限制和超时响应导致。作者详细解释了如何通过检查内存使用情况和调整配置解决内存满的问题,以及Pulsar的发送机制和超时处理过程。
摘要由CSDN通过智能技术生成

记一次pulsar数据丢失排查

背景

生产者往pulsar写消息时会有递增的序列号字段,消费端在消费时,会出现序列号断层。当下无法确定是生产端、mq、消费端哪个地方丢失了数据,所以先从生产端进行排查。
生产端的消息发送是通过sendAsync的异步方法Template.sendAsync该方法会返回一个CompletableFuture对象目前是没有对该对象进行检查,所以补上对应的代码逻辑

template.sendAsync(xxx, protocol)
                    .whenCompleteAsync((res, exp) -> {
                        if (exp != null) {
                            log.error("", exp);
                        }
                    });
        }

在运行一段时间后,该异常被触发,可以确定问题在生产者这里了

异常排查

内存限制

运行一段时间后提示Client memory buffer is full,观察源码得知ProducerImpl在发送消息给mq时,会先进行this.canEnqueueRequest(callback, message.getSequenceId(), uncompressedSize)判断消息是否可以发送。
这里判断的是使用的本地内存大小

if (!this.client.getMemoryLimitController().tryReserveMemory((long)payloadSize)) {
                    this.semaphore.ifPresent(Semaphore::release);
                    callback.sendComplete(new PulsarClientException.MemoryBufferIsFullError("Client memory buffer is full", sequenceId));
                    return false;
                }

ProducerImpl内部会有一个流量控制器,对当前消息的大小进行累加,判断是否超过限制,默认大小为64M。于是乎这边对限制的大小进行调大,在我们创建PulsarClinet时可以指定如下参数设置使用本地内存大小的最大值,这里我将大小调整
memoryLimit(512, SizeUnit.MEGA_BYTES)
重启后不在提示该异常

超时响应

虽然解决了内存问题,新的异常又开始出现
can not send message to the topic persistent://public within given timeout : createdAt 5.207 seconds ago, firstSentAt 5.206 seconds ago, lastSentAt 5.206 seconds ago, retryCount 1
在讲解这个异常怎么检测前,先说说ProducerImpl的发送机制,发送的数据会被加入到ProducerImplOpSendMsgQueue对象中
在这里插入图片描述
在pulsar中间件处理完数据后会告诉回发送端,这时ClientCnx netty对象就会回调handleSendReceipt方法将OpSendMsgQueue的数据进行移出,就是告诉发送端这条消息存储成功的一个响应-应答模式
在这里插入图片描述
那如果消息一直没有ack,这时发送端就会有个内部定时器进行定时检测,其在ProducerImpl.run方法中,默认执行时间为30s,会判断pendingMessage中的第一条消息是否超过了30s的时间,如果超过了,就会进行failPendingMessages方法调用

                        long diff = this.conf.getSendTimeoutMs() - TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - firstMsg.createdAt);
                        if (diff <= 0L) {
                            log.info("[{}] [{}] Message send timed out. Failing {} messages", new Object[]{this.topic, this.producerName, this.pendingMessages.messagesCount()});
                            PulsarClientException te = new PulsarClientException.TimeoutException(String.format("The producer %s can not send message to the topic %s within given timeout", this.producerName, this.topic), firstMsg.sequenceId);
                            this.failPendingMessages(this.cnx(), te);
                            timeToWaitMs = this.conf.getSendTimeoutMs();
                        } else {
                            timeToWaitMs = diff;
                        }

failPendingMessages方法中,会调用每个消息的sendComplete方法进行异常回调,也就是我们最初加在sendAsync上的回调信息了。
自此调用链路就比较清晰了

  1. 生产端发送消息给pulsar,pulsar接受成功后会告诉生产端
  2. 生产端每隔30s会对pendingMessages队列进行检查
  3. 如果第一条消息发送的时间超过30秒,就代表pulsar 30秒还未对该消息进行ack
  4. 生产端提示超时异常
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值