本来写blog的时候并没有想到会有多少人看到,所以,只是把所想的写下来。后来,发现有很多热心的人给我的问题提了很多意见,心里很是感激。想想同仁的意见如果可以帮我改进JTangMQ的设计,那真得是求之不得的事情,亦或是我写的东西对其他人也有参考价值,那是我的莫大荣幸了。那就继续把所想的写下来吧。
上篇提到,消息cache引出的两个问题令人十分头痛,它的的确确影响着JTangMQ的性能。为了解决第二个问题——临界资源消息cache带来的影响,前两天想了一个办法,这个办法有点土,就是把对消息cache的操作交给一个线程池来操作,以此来减少对发送线程&接收的线程的影响。为什么我会这样做?怎么做?这样做又会带来什么样的结果呢?
首先来看看为什么要这么做。前面的几篇文章里提到过JTangMQ的通讯层采用的是nio的技术。一位同仁在评论中也提到采用nio的目的就是为了使用nio的非阻塞多路复用技术来减少线程数。所以,在JTangMQ的服务器端会有一定数量的读写线程来并发为成千上百个的连接服务。现在我们来假设一种极端的情况,就是服务器端只采用一个读线程和一个写线程来处理所有从客户端到服务器端的连接。如果此时有100个消息发送者和100个消息接收者连接到了服务器。100个消息发送者向服务器端的唯一的这个读线程中的选择器注册了读操作,然后同时开始往服务器端不停地发送消息。此时,服务器读线程的选择器相应地选中了100个来自客户端的ReadableChannel,然后依次轮流对这100个channel进行读操作。读线程首先从第一个channel中读到字节流,通过解码器把字节流decode成数据报,再经过相应的处理获得消息,然后获得消息cache上的锁,把消息加入到cache,释放cache上的锁,接着再经过相应的处理把消息加入到某个目的地(队列或主题)中后完成了一个channel的读任务,接着处理第二个channel,依次完成100个。与此同时,100个消息接收者向服务器端的写线程注册了写操作(这里为了方便起见假设直接注册了写操作,在实际过程中并不能这样做,是要按需注册写操作的,否则会引起cpu被写操作霸占的恶劣情况),并开始接收消息。在读线程不断把消息加入到目的地的同时,接收者从目的地中取消息,取到消息后,交给写线程发回到到相应的接收者,接收者处理完消息,发送一个应答到服务端,服务器的读线程读到这个应答消息,然后获得消息cache上的锁,删除这个消息,释放锁,接着再到目的地把这个相应的消息删除,完成一个接收过程(实际过程比这个复杂得多,这里只是把相关的关键过程描述了一下)。从以上的过程我们可以注意到这个临界资源消息cache的访问问题,就是读写操作线程必须竞争以获得对cache的访问权限,这样,原本可以并发的操作就成了顺序操作,直接导致了读写两个的线程的效率降低。
(夜深了,明天继续)