之前介绍了客户端的发送实现,客户端发送到服务端,服务端怎么处理消息的我们看一下源码实现
一、发送消息服务端入口
public class ServerCnx {
protected void handleSend(CommandSend send, ByteBuf headersAndPayload) {
// 获取创建时存储的生产者
CompletableFuture<Producer> producerFuture = producers.get(send.getProducerId());
Producer producer = producerFuture.getNow(null);
// 校验非持久化的等待消息数是否超过最大值默认1000
if (producer.isNonPersistentTopic()) {
if (nonPersistentPendingMessages > maxNonPersistentPendingMessages) {
final long producerId = send.getProducerId();
final long sequenceId = send.getSequenceId();
final long highestSequenceId = send.getHighestSequenceId();
service.getTopicOrderedExecutor().executeOrdered(producer.getTopic().getName(), SafeRun.safeRun(() -> {
commandSender.sendSendReceiptResponse(producerId, sequenceId, highestSequenceId, -1, -1);
}));
producer.recordMessageDrop(send.getNumMessages());
return;
} else {
nonPersistentPendingMessages++;
}
}
// 校验当前发送速率和等待发送的消息是否超过最大值
startSendOperation(producer, headersAndPayload.readableBytes(), send.getNumMessages());
// 省略事务相关
// HighestSequenceId和SequenceId
// 客户端会批量发送,一批里每个消息SequenceId都不一样(一批也可能只有一条),有大有小
// 一批中最大那个是highestSequenceId,最小的是lowestSequenceId,在这里最小的也是send.getSequenceId()
// 因为批量消息的SequenceId设置的是第一条添加进来的消息SequenceId
if (send.hasHighestSequenceId() && send.getSequenceId() <= send.getHighestSequenceId()) {
// 批量发送
producer.publishMessage(send.getProducerId(), send.getSequenceId(), send.getHighestSequenceId(),
headersAndPayload, send.getNumMessages(), send.isIsChunk(), send.isMarker());
} else {
// 单条
producer.publishMessage(send.getProducerId(), send.getSequenceId(), headersAndPayload,
send.getNumMessages(), send.isIsChunk(), send.isMarker());
}
}
}
重点在producer.publishMessage(...),我们分析最复杂的场景批量发送。
二、Producer中的发送,第一层回调MessagePublishContext
public class Producer {
public void publishMessage(long producerId, long lowestSequenceId, long highestSequenceId,
ByteBuf headersAndPayload, long batchSize, boolean isChunked, boolean isMarker) {
// 最小比最大还大肯定客户端的批量发送有问题
if (lowestSequenceId > highestSequenceId) {
cnx.execute(() -> {
cnx.getCommandSender().sendSendError(producerId, highestSequenceId, ServerError.MetadataError,
"Invalid lowest or highest sequence id");
cnx.completedSendOperation(isNonPersistentTopic, headersAndPayload.readableBytes());
});
return;
}
// 主要验证消息的完整性、加密、统计发送速率用于发送限流
if (checkAndStartPublish(producerId, highestSequenceId, headersAndPayload, batchSize)) {
// 发送
publishMessageToTopic(headersAndPayload, lowestSequenceId, highestSequenceId, batchSize, isChunked,
isMarker);
}
}
}
可以看到重点在publishMessageToTopic(...)
private void publishMessageToTopic(ByteBuf headersAndPayload, long lowestSequenceId, long highestSequenceId,
long batchSize, boolean isChunked, boolean isMarker) {
// 最终还是topic调用发送
// MessagePublishContext 把发送的参数都放入到这个请求上下文对象
// 同时它也是发送的回调函数
topic.publishMessage(headersAndPayload,
MessagePublishContext.get(this, lowestSequenceId,
highestSequenceId, msgIn, headersAndPayload.readableBytes(), batchSize,
isChunked, System.nanoTime(), isMarker));
}
接着分析持久化topic的发送
非持久化实现:接到消息,找到topic上订阅的消费者分发给它,如果存在对等集群,同步一份过去。没了
三、Topic中的发送,第二层回调PersistentTopic
@Override
public class PersistentTopic {
public void publishMessage(ByteBuf headersAndPayload, PublishContext publishContext) {
// 等待发送的消息数+1,减1正常是在发送成功的回调里,后面会分析
pendingWriteOps.incrementAndGet();
// topic可能被删了
if (isFenced) {
publishContext.completed(new TopicFencedException("fenced"), -1, -1);
// 上面说的等待数减1,后面的过程有异常都得减1
// 还有:当前topic关联的所有生产消费者全部断开关闭,当前ledger关闭,Bundle关闭,同步到对等集群
// 一句话:topic相关的基本全部清空
decrementPendingWriteOpsAndCheck();
return;
}
// 校验消息是否超过配置的最大
// 该配置精确到topic级别
if (isExceedMaximumMessageSize(headersAndPayload
Pulsar消息发送流程

本文详细解析了Pulsar消息系统中消息从客户端发送到服务端的整个过程,包括客户端和服务端的主要处理逻辑,消息的批量发送及持久化机制,并深入探讨了涉及的多层回调机制。
最低0.47元/天 解锁文章
2509





