一. server端服務啓動流程
-
創建一個Processor(rpc_handler)實例,用於處理特定的輸入;
-
TBufferedTransportFactory用於數據傳輸;
-
threadManager.start()啓動服務線程;
-
Protocol表示數據傳輸的上層協議;
-
Server類用於接受客戶端請求。
二. threadManager.start()
這個方法主要用於啓動服務端的工作線程。
1. 調用addWorkder啓動workerCount_個工作線程;
a) 創建workerCount_個ThreadManager::Worker和BoostThread相互引用。
b) 設置ThreadManager::Worker的狀態爲STARTING。
c) 調用每個BoostThread的start方法;
i. 創建boost::thread,指定入口函數爲threadMain;
ii. 設置BoostThread的狀態爲starting;
d) 將BoostThread加入idMap中;
e) 等待所有的線程啓動完成。
2. boost::thread的入口函數threadMain():
threadMain()函數是工作線程的入口函數,主要功能是取得threadManager的(std::set<Task*> tasks_;)中的task實例並進行處理,其具體實現步驟如下所示:
從入口函數的參數中得到BoostThread指針;
a) 設置BoostThread的狀態爲started;
b) 調用ThreadManager::Worker的start方法,循環處理tasks_中的task;
i. 檢查workerCount是否達到最大值;
ii. 循環處理以下情況;
1. 沒有task需要處理, manager_->monitor_.wait();等待處理task
2. 有task需要處理,則取得task_中的第一個task,設置task的狀態爲EXECUTING,調用task->run()處理任務;
c) 線程退出,設置BoostThread的狀態爲stopped。
三. TThreadPoolServer.serv()
主線程:主要用於接收客戶端的請求並組成task並加入到threadManager的task_中,工作線程取得task並進行處理。 TThreadPoolServer.serv()的具體實現:
1. 調用serverTransport_->listten()方法,serverTransport是serverSocket類型的對象。
a) 嘗試連接
b) 創建socket,bind,listen.
2. serverTransport_->accept(); 同步等待客戶端的連接,連接成功則轉到步驟3;
3. 利用工廠得到transport對象和protocol對像;
4. 創建TThreadPoolServer::Task對象,傳入server,processor,protocol,transport,client對象:
server對象爲TThreadPoolServer類型,
processor對象爲FileStorageProcessor對象,
protocol爲TBinaryProtocol類型,
transport對象爲TBufferedTransport類型,都可以通過TThreadPoolServer構造函數配置。
client對象爲accept返回的socket
5. 將TThreadPoolServer::Task對象插入到threadManager的任務隊列中;
a) 刪除過期的任務,本例中將過期時間設置爲0表明不過期;
b) 檢查pending 任務的數量是否超過最大值;
c) 將task加到任務隊列中;
d) 如果有空閒的thread,就喚醒一個線程處理。
四. TThreadPoolServer::Task::run()
這個函數在工作線程中調用,主要作用:循環讀取消息頭,得到version,函數名和messageType,並且調用對應的處理函數得到輸出傳給客戶端,循環直到socket連接斷開。如果socket不斷開,就一直循環。
A. 循環讀取消息頭並處理
1. 得到EventHandler和callContext;
2. 處理callContext;
3. 循環調用FileStorageProcessor::process處理;
a) iprot->readMessageBegin(fname, mtype, seqid)得到函數名,MessageType和seqid
i. result += readI32(sz); 得到version
ii. result += readStringBody(name, sz); 得到fname
iii. result += readByte(type); 得到MessageType
4. 如果scoket連接中斷則則出循環
5. process_fn(iprot, oprot, fname, seqid, callContext); 調用特定的函數處理;
2
B. Protocol怎樣從socket讀取數據
ReadI32讀取4個字節。readString會先讀取size(4個字節),然後讀取string
具體步驟:
1. 建立結構體,便於轉換字節序;
2. 調用TBufferedTransport::readAll讀取4個字節,直接調用TBufferBase::readAll。
a) 檢查rBuffer中是否有數據,有數據則直接中rBuffer中獲取;
b) 沒有數據則調用Transport::readAll;
i. 調用TBufferBase::read,檢查rBuffer中是否有數據,沒有則從socket中讀取,transport_就是保存的socket。
ii. 將讀取的數據轉換爲主機字節序。
C. 對應函數名的特定處理函數
函數名對應的處理函數存放在processMap_中,如”WriteFile”對應的處理函數爲processMap_["WriteFile"] = &FileStorageProcessor::process_WriteFile;
具體步驟:
1. 調用args.read(iprot);讀取參數
a) 調用readFieldBegin得到參數type和參數id;
b) 得到參數的值
i. 調用具體的RPC方法,並得到結果result.success
ii. 調用oprot->writeMessageBegin("WriteFile", apache::thrift::protocol::T_REPLY, seqid);寫入messagetype;
iii. 調用result.write(oprot)寫入結果,通過socket傳輸。