RPC Server的实现用到了Thread,Cocurrency,NIO等技术,对于学习者来说是很好的实践参考。这里先把workflow理清楚,再去研究具体的实现细节。
如下图所示,其主要涉及到以下几种线程:
- Listener线程:以selectServerSocketChannel的方式监听Client的connect请求;对于每一个Client请求,由ServerSocketChannel创建一个SocketChannel;
- Reader线程池:以selectSocketChannel方式监听Client的send请求,每一个Reader线程拥有自己的一个selector;
- Handler线程池:Reader接受请求,封装成Call,然后放入CallQueue,随后由一个Handler取出进行处理。Handler不仅负责调用真正的方法实现,还将尝试向SocketChannel写入返回值;
- Responder线程:如果Handler无法将Call的Response写入SocketChannel发送给Client(由于网络原因,或Socket的output buffer已满等),将由Responder线程负责余下的写入尝试。
大致的工作流程如下:
- Listener线程的selector在ServerSocketChannel上注册OP_ACCEPT事件,并且创建Reader线程池。每个Reader的selector此时并不监听任何Channel;
- Client发送TCP连接,触发Listener的selector唤醒Listener线程;
- Listener调用ServerSocketChannel.accept()创建一个新的SocketChannel;
- Listener从Reader线程池中挑选一个,将选中Reader的selector在新建的SocketChannel上注册OP_READ事件;
- Client发送TCP数据包,触发Reader的selector唤醒Reader线程;
- Reader从SocketChannel中读取数据,封装成Call,然后放入CallQueue;
- 最初,Handler线程池中的线程都block在CallQueue(调用BlockingQueue.take())上,当有Call被放入后,其中一个Handler线程被唤醒,然后根据Call的信息调用对应的实现方法。随后,Handler尝试将Response写入SocketChannel;
- 如果Handler发现无法将Response完全写入SocketChannel,将让Responder的selector注册OP_WRITE事件。当socket恢复正常,可以继续尝试了,Responder将被唤醒,继续做尝试。当然,如果一个Call Response在一定事件内都无法被写入,会被Responder移除。
理清了Server的线程模型和基本工作流程后,接下来就可以分析每一步的具体代码实现,后面会继续学习然后总结。