题目:
设计一个消息中间件,有N个生产者,M个消费者。第一阶段N个线程代表N个生产者,并且每个消息都属于某一个topic,或则是queue同时发送消息给消息中间件,并落盘,然后将N个线程kill掉;第二阶段,M个线程代表M个消费者,每个消费者会订阅多个queue和toptic,将订阅的消息从盘上读出来。
总结:
- 会产生大量对象,对象重用
- 如何做大无锁或者是最小粒度的加锁,使得N个线程并发写入数据
- 数据压缩
- 快速序列化
- 零内存拷贝或减少内存拷贝
- 读线程,批量读取
- 缓存(事实证明效果不大)
解题思路:
文件落盘组织方式
queue或者topic统称为bucket
属于同一bucket的消息作为一个逻辑文件保存,逻辑文件表示由一堆小文件组成,文件大小大概为8M。如果将一整个bucket看成一个文件的话,那么读文件一次将整个文件载入内存,对内存的消耗太大,吃不消。而且如果分成小文件的话,写入的时候如果出现故障的话,也只是一个小文件落盘损失。
MappedByteBuffer处理写和读
因为这个使用的是堆外内存,操作系统可以完成刷盘操作。而且对于读的过程,不同的消费者很大的可能会需要读取相同的文件,而该接口在操作系统层面做了pageCache优化。
ThreadLocal<Map<String,ByteBuffer>>
每个线程都有一个自己的Map<string,byteBuffer>, 当个queue或者topic的byteBuffer满了之后,批量提交将byte[]数据写入string的file
序列化
message的head和property都是key-value的方式保存,而body是byte[],只需要序列化key-value。key是string,value有Int,long,double,string,那么需要增加一个byte表示value的type类型,同时保存数据的长度即可。
压缩算法
比较了snappy算法和lz4算法,整体来说,两个算法都是非常好的压缩和解压的速度,相比来说,lz4性能会好一些。