Put消息时序图分析
首先,我们从PutMessage的时序图开始。部分时序图如图1所示。
图1 PutMessage部分时序图
以上部分可以简单理解为以下4步:
1、获取可写入的文件。MapedFileQueue.java中getLastMapedFile()方法;
2、进行内存映射(mmap),将可写文件映射到内存缓冲区。之后应用程序与操作系统共享这块缓冲区。具体原理如下图2所示。
图2 mmap原理
参考链接:http://www.penglixun.com/tech/system/linux_cache_discovery.html
http://www.dongsm.cn/metaq%E9%9B%B6%E6%8B%B7%E8%B4%9D%E6%8A%80%E6%9C%AF%EF%BC%881%EF%BC%89/
3、向内存缓冲区追加消息。(包括找到追加位置、封装消息、回调追加方法等流程)
4、追加消息成功之后,唤醒刷盘服务。
源码走读
1、broker从网络上收到消息并解码之后,开始进行put消息的过程。DefaultMessageStore中putMessage调用如下。
2、获取到可写入文件,并进行内存映射之后,开始向内存缓冲区追加消息。
3、找到追加位置,从内存缓冲区中切分出可写入数据的区域,并由byteBuffer句柄指向这块区域。
其中mappedByteBuffer即为可写文件在内存中的映射,mappedByteBuffer与byteBuffer的关系如下图。
4、之后,对消息进行一系列的封装,并最终通过调用byteBuffer,put方法,将封装后的消息追加入内存缓冲区。
分析
1、当消息较小,即大量消息小于1k时,每获取到一条小消息,都将进行byteBuffer.put的操作,put操作将被频繁调用。
优化
1、优化思想的核心是减少put的调用,尽量每一次put都写入更多的数据。以下是简化的流程图。
当新来一个消息,首先判断该消息与队列中已经存在的消息的字节总数(消息体)是否超过4k,如果超过,则将队列中的消息依次进行封装,并将所有的封装后的消息一次性追加入内存缓冲区中。
考虑到并发场景,sizeCount采用单独设计的非阻塞的计数方式,队列采用ConcurrentLinkedQueue。
具体代码还在测试当中,后续文章将整理代码过程及测试结果。。
参考文章:http://hellosure.iteye.com/blog/1126541
http://www.ibm.com/developerworks/cn/java/j-jtp04186/
http://www.cnblogs.com/linjiqin/archive/2013/05/30/3108188.html