Netty2020 3-9 Netty源码之flush优化

1.Netty的flush优化handler——FlushConsolidationHandler

Netty2020 3-9 Netty源码之flush优化_第1张图片

Netty2020 3-9 Netty源码之flush优化_第2张图片

Netty2020 3-9 Netty源码之flush优化_第3张图片

Netty的实现更复杂一些,对于用单独线程池处理业务的场景,有一些特殊的处理和优化,比如如果异步处理的时候,即channelReadComplete比channelRead结束的要早,所以在flush调用的时候,readInProgress已经是false了,然后根据用户决定是否开启对于异步的强化来决定是直接flush还是走consolidateWhenNoReadInProgress,如果consolidateWhenNoReadInProgress为true(开启增强),那么就用次数是否到达阈值来决定立即刷新还是延时刷新。

我们可以写一个demo来尝试一下,这里我们为业务单独指定线程池,同时开启异步增强

关于标志位,首先channelRead方法中会把readInProgress置为true,代表正在读,还没有执行channelReadComplete方法,此时flush是同步,因为还没到channelReadComplete的时候。

然后channelReadComplete方法中把它又置为false,说明channelReadComplete方法执行完了,此后的flush都是异步了,因为业务执行慢,错过了channelReadComplete。

再看这些count,这里第一次flushPendingCount++是1,不满足阈值,所有走到scheduleFlush之中,虽然该方法中没有延时,但是毕竟是单独提交任务,所以给了其它请求write的机会,所以也就有可能节省flush的次数,这里为了让scheduleFlush更慢,我们在这个断点处停留一下,相当于卡住,不让它schedule(调试技巧)。同时客户端因为又发了请求,所以这第二个请求又会触发到这里的flush

可以看到会走到代码断点处,这里就会触发flushNow,这里会进行flush,并取消掉scheduleFlush这个任务,说明这两个请求的flush合并为一个flush了,节省了一次flush。

2.Cassandra的flush优化——Dispatcher#Flusher

我们参考Cassandra的实现,https://github.com/PaytmLabs/cassandra/blob/master/src/java/org/apache/cassandra/transport/Message.java,去除Cassandra的编解码和连接的部分,自己写一个flush的优化器

Netty2020 3-9 Netty源码之flush优化_第4张图片

Netty2020 3-9 Netty源码之flush优化_第5张图片

Netty2020 3-9 Netty源码之flush优化_第6张图片

Netty2020 3-9 Netty源码之flush优化_第7张图片

然后和Netty的Consolidation对比一下,可以发现Cassandra的这个优化器的思路更为简单清晰。它就是利用了队列来保存待flush的内容,在达到flush条件的时候把队列里存储的需要flush的内容一次性flush出去。

flush的条件

flush的条件有两个,一个是runsSinceFlush,还有一个是flushed的元素个数,两者有其一满足就可以flush,这样可以防止两种场景:

①请求量很大,在run方法执行3次以前,flushed中已经积攒了太多的需要flush的内容,所以要设置flushed的阈值

②请求量很小,flushed很久才会达到阈值,这样要等很久才能把之前积攒的消息flush出去,会有很大延时,所以要设置runWithNoWork的阈值

结合这两个设置就可以避免这两种极端情况出现了。然后它还可以通过为eventLoop这个executor添加延时任务的方式循环调用该run方法,可以更精细的控制这个flush的流程。

自毁功能

然后这个flusher还有一个自毁功能,如果runsWithNoWork超过5次,说明连续5次都没有发现要flush的内容,那么就判断是否要结束该run方法,并不再schedule。

这里有两个判决条件:待flush的队列是否为空,running.compareAndSet(false,true)是否失败,两者满足其一就可以结束run执行并不再schedule。

队列为空比较容易理解,因为没有待flush的任务了。那么running.compareAndSet(false,true)是否失败是什么意思呢?我们看到Flusher的start方法中,会判断running标志位,如果running是false,同时running.compareAndSet(false,true)成功,就可以将该run作为task用EventLoop来执行。那么假设这里有两个线程,线程A执行到runsWithNoWork超过5次的逻辑了,那么它会先将running设置为false,然后执行running.compareAndSet(false,true),另一个线程B执行到start方法中的running,首先会判断它是false,然后也执行running.compareAndSet(false,true),这两个只会有一个成功。

如果线程A成功了,那么说明线程B的start没有成功,但是确实又有任务需要添加到flush的队列中,所以run方法不能就此终止,所以这里逻辑也是和这里的分析吻合的,这时不能return,要继续schedule该run方法。

如果线程A没成功,那么说明线程B的start成功了,既然已经成功又为EventLoop添加了该run方法这个任务,那么A线程正在执行的任务就没必要保留了,那么这里的逻辑也和分析吻合,这时就会return,结束run方法,同时不再schedule。

3.总结

综上,可以看出Cassandra的flush优化实现思路更加清晰简单,但简单的同时又不失全面,可以说考虑各种极端情况和场景,并且通过参数的可配置,让用户可以定制flush的控制逻辑,如果有flush优化场景的工程可以借鉴使用。

 

你可能感兴趣的:(netty,netty)