首先我們來分析一下一個典型的RPC服務器短的處理流程,如下圖所示:
從圖中可以清楚的看到當客戶端向服務器端發送一個請求的時候,最開始是被RPCServer中的Listener所監聽到的,如下面的代碼所示,HBase的RpcServer啓動的時候會啓動幾個處理線程:
responder.start();
listener.start();
scheduler.start();
其中,Responder線程負責數據的request的回覆工作,listener負責監聽客戶端的請求,scheduler負責具體call的調度工作 while (running) {
SelectionKey key = null;
try {
selector.select(); // FindBugs IS2_INCONSISTENT_SYNC
Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
while (iter.hasNext()) {
key = iter.next();
iter.remove();
try {
if (key.isValid()) {
if (key.isAcceptable())
doAccept(key);
}
} catch (IOException ignored) {
if (LOG.isTraceEnabled()) LOG.trace("ignored", ignored);
}
key = null;
}
}
listener的實現過程中運用了java nio的一些特性,主要是每個Listener線程又會管理這一個Reader的線程池,這些Reader具體負責從Socket Channel中讀取數據,並解析數據中的相關項,進而構造出可運行的CallRunner: Call call = new Call(id, this.service, md, header, param, cellScanner, this, responder,
totalRequestSize,
traceInfo);
scheduler.dispatch(new CallRunner(RpcServer.this, call, userProvider));
RpcScheduler調用對應的RpcExcutor進行相應的處理,RpcExcutor中啓動了多個處理線程,這些線程從隊列中取出任務並且執行, protected void startHandlers(final String nameSuffix, final int numHandlers,
final List<BlockingQueue<CallRunner>> callQueues,
final int qindex, final int qsize, final int port) {
final String threadPrefix = name + Strings.nullToEmpty(nameSuffix);
for (int i = 0; i < numHandlers; i++) {
final int index = qindex + (i % qsize);
Thread t = new Thread(new Runnable() {
@Override
public void run() {
consumerLoop(callQueues.get(index));
}
});
t.setDaemon(true);
t.setName(threadPrefix + "RpcServer.handler=" + handlers.size() +
",queue=" + index + ",port=" + port);
t.start();
LOG.debug(threadPrefix + " Start Handler index=" + handlers.size() + " queue=" + index);
handlers.add(t);
}
}
protected void consumerLoop(final BlockingQueue<CallRunner> myQueue) {
boolean interrupted = false;
double handlerFailureThreshhold =
conf == null ? 1.0 : conf.getDouble(HConstants.REGION_SERVER_HANDLER_ABORT_ON_ERROR_PERCENT,
HConstants.DEFAULT_REGION_SERVER_HANDLER_ABORT_ON_ERROR_PERCENT);
try {
while (running) {
try {
CallRunner task = myQueue.take();
try {
activeHandlerCount.incrementAndGet();
task.run();
其實繼續跟進代碼就會發現CallRunner的run方法最主要動作有兩個一個是獲取操作結果一個是返回操作結果給客戶:
resultPair = this.rpcServer.call(call.service, call.md, call.param, call.cellScanner,
call.timestamp, this.status);
if (!call.isDelayed() || !call.isReturnValueDelayed()) {
Message param = resultPair != null ? resultPair.getFirst() : null;
CellScanner cells = resultPair != null ? resultPair.getSecond() : null;
call.setResponse(param, cells, errorThrowable, error);
}
call.sendResponseIfReady();
到此結束就簡要介紹了一個完整的服務端RPC處理流程,該流程中涉及到的相關的類的關係如下圖所示: