前言
最新的4.8.0的DLedger模式中在处理发送消息的时候,从节点ack通过流水线的方式,大大的提升了消息发送的吞吐量。
在先前的版本里(4.8.0版本之前,如4.7.1),关于DLedger模式,我查看源码发现存在以下几个情况,其中部分在4.8.0中尚未优化。
详细说明
主从间的网络对发送tps产生影响
因为提交消息的时候需要同步阻塞等待法定人数的从节点的ack才能返回,对于消息发送的性能影响非常大。
/**
* Handle the append requests:
* 1.append the entry to local store
* 2.submit the future to entry pusher and wait the quorum ack
* 3.if the pending requests are full, then reject it immediately
*
* @param request
* @return
* @throws IOException
*/
@Override
public CompletableFuture<AppendEntryResponse> handleAppend(AppendEntryRequest request) throws IOException {
// 前面删除了很多代码
DLedgerEntry dLedgerEntry = new DLedgerEntry();
dLedgerEntry.setBody(request.getBody());
DLedgerEntry resEntry = dLedgerStore.appendAsLeader(dLedgerEntry);
return dLedgerEntryPusher.waitAck(resEntry, false);
//后面删除了一些代码
}
在4.8.0中通过流水线将ack的动作异步化,可以很快的释放发送消息的工作线程处理后面的发送消息请求。
事实上,这个ack的动作在一些实际场景中,网络问题可能成为发送tps的瓶颈。打个比方,我使用的云虚拟机,部署rocketmq集群的节点不在一个网段,实际宿主机基至可能离的比较远,我ping了下,发现时间竟然需要0.3-0.4ms,这意味,一个请求动作可能就需要消耗最少0.3ms,而0.3ms只是ping的时间,实际该动作请求rt可能更长,就算按1ms平均可以处理3个tcp请求,那1s最多也就3000个。1个ack应该是最少需要两次通信(请求+应答)这就意味着我的发送tps,单节点发送tps根本就不可能超过3000/2,tps就只有1500了,我的环境进行压测,不到1000的发送tps.
另一个问题是如果从节点有问题也对master的发送tps影响很大,我模拟其中一个从节点挂掉,tps更低了。
在使用4.8.0的优化中,DLedger的发送tps约是非DLedger的一半,比如普通模型是6W,DLedger能达3w,对于我的环景是很恐怖的提升。
老版本怎么解决
如果在4.7.1中,DLedger的发送tps这么低,解决办法是修改默认的发送端线程数配置,并且将自旋锁修改为重入锁,通过增加线程来解决IO的阻塞导致的CPU使用率下降,只要发送端线程数设置合理,发送tps还是很可观的,这两项配置看之前写的这篇:RocketMQ的broker处理消息commit时,加锁应该使用自旋锁还是重入锁。4.8.0使用默认的这两项配置即可,基本不用考虑再增加线程数。
尽管4.8.0之前的版本通过增加线程数能解决发送tps低的问题,但是因为ack的时候,与从节点的网络闪断,也可能导致put message持有锁过久,也即我们常见的system busy,触发限流。最好的建议是升级4.8.0版本。
主从没有异步复制的区别
DLedger模型在4.8.0之前的版本,没有主从同步双写或异步消息复制的区别,都是同步阻塞方式,在DLedgerCommitLog类里,都是调用的putMessage方法,没有异步化的操作:
@Override
public CompletableFuture<PutMessageResult> asyncPutMessage(MessageExtBrokerInner msg) {
return CompletableFuture.completedFuture(this.putMessage(msg));
}
@Override
public CompletableFuture<PutMessageResult> asyncPutMessages(MessageExtBatch messageExtBatch) {
return CompletableFuture.completedFuture(putMessages(messageExtBatch));
}
所以建议升级4.8.0版本。
老版本不支持堆外内存
DLedger模型的另一个问题是在put message的时候不支持使用堆外内存,这个问题目前在4.8.0中还没修复.非DLedger通过配置参数:transientStorePoolEnable
可以选择启用堆外内存,默认会额外占用5个G,发送消息处理当提交消息的时候可以先写入堆外内存,异步写入page cache,然后刷盘等,读取还是从page cache,实现读写分离。DLedger目前还是通过mmap直接写入page cache。当时我看源码发现这个问题的时候还很奇怪,跟作者确认了下确实不支持这个配置。哎,亏我之前还让老大在生产开启这个配置,设置了3G内存,结果就是浪费。
总之,如果使用DLedger还是强烈建议升级4.8.0。