RPC框架的Server:
(1)服务器需要一个listen线程(Listen内部类),需要一个已接受连接的列表(connectionList)
(2)连接对象(Connection)需要包含和客户端连接的通道(SocketChannel),readAndProcess方法读取client端发过来的信息完成hrpc和version比较并做分头信息和调用信息分别处理,
(3)processHeader用来处理头信息,会得到根据传来的协议字符串获得协议类(class<?>),会根据UserGroupInformation得到Subject user(Principal,Public credentials,Private credentials)并设置user的身份(用户名,所有组名)
(4)processData用来处理call.id+call.param,生成Call对象放到Server的BlockingQueue<Call> callQueue待处理。
(5)doStop方法用来做打断Listen的run方法中循环的清理工作,run方法会中的BlockingQueue操作可能被打断等等,控制线程会首先尝试把run方法打断,然后调用doStop方法,doStop会获得Listen对象锁,如果发现selector不为空,说明listen的run方法仍然阻塞在select调用,这时要调用selector.wakeup(),并调用Thread.yield()稍等一会让run方法运行到synchronized部分,之后由run方法的后半部分进行selector关闭等资源回收工作。总之这个方法用来使Listen的run方法跳出循环。
(6)Handler线程从BlockingQueue中拿出Call对象调用Subject.doAs完成方法调用。
(7)Responder线程:每一个connection中都有一个response队列(LinkedList<Call>),processResponse负责处理这样的队列,从队列中拿出一个Call,首先尝试把Call中的返回数据都发送出去,如果没有一次性发送出去那就复杂了,因为这时需要把connection中的channel放到Responder中的write监视中来,如果这个Call是connection中第一个要回复的成员。而且还要知道调用processResponse方法的是那个线程,因为Handler线程和Responder线程本身都可能调用,但是Handler线程不能在注册selector上面延迟太长时间,假设现在Responder线程阻塞在select调用上面,那么Handler为了尽快完成注册返回首先增加信号量pending通知Responder有线程要注册,然后调用wakeup使responder从select调用中尽快返回,注册好后要减少信号量。
(8)RPC的Server中把真正在Handler线程上跑的远程调用方法采用发射机制完成调用,而且覆盖了authorize方法,使得connection在获得头信息时校验用户。
(9)总结,Server有Listen线程负责监听新连接和读入远程调用参数以及头信息,待校验头信息无误(包含版本以及用户是否授权使用协议)把Call放入BlockingQueue排队。Handler方法从BlockingQueue中拿到Call执行远程调用,执行完毕后,如果Call对应的connection的response队列首次获得一个返回结果,需要在Responder中的selector中注册connection的channel。Responder线程就是不断地从channel对应的connection中的response队列写回客户端。