SimpleRpc-客戶端與服務端工作模型探討

haolujun發表於2017-09-21

前言


本篇文章講述客戶端與服務端的具體設計細節。有細心的小夥伴發現,客戶端和服務端的工作方式不一樣:服務端是多執行緒計算模型,利用工作執行緒完成資料的讀取,而客戶端是單執行緒(利用Reactor執行緒完成資料的讀取)。這麼做的原因有二:首先我們認為我們的使用RPC的初衷是由於CPU計算是瓶頸,不得已把計算放到多臺機器上,所以服務端採用多執行緒計算模型;其次我們認為網路IO只要不是客戶端故意阻塞,那麼無論是請求資料還是響應資料只需要一次接收就可以收全,不會有執行緒長時間阻塞在網路上,所以客戶端就使用反應器執行緒進行接收響應資料。

客戶端同步和非同步呼叫


SimpleRpc提供了同步呼叫和非同步呼叫的方法,使用區別在於傳遞的引數不同,如下所示。

    //非同步請求
    int async_request(Server &server, Request *request, Response *response, ResultHandler *handler);                 
    //同步請求
    int sync_request(Server &server, Request *request, Response *response);

那麼SimpleRpc對於同步和非同步呼叫是如何支援的呢?我們重新看一下DownstreamHandler對資料的處理方式:

void DownstreamHandler::handle_read(int fd) {                                                                   
  char head[4];
  Connection conn(fd);
  conn.recv_n(head, 4);
  int size = *((int *)head);
  char *buf = new char[size];                                                                                        
  conn.recv_n(buf, size);                                                                                            
  close(fd);  
  printf("Downstream Handler close fd:%d\n", fd);                                                                    
  //下游響應
  _response->deserialize(buf, size);                                                                                 
  //如果有result_handler,則呼叫data_comeback鉤子函式
  if(_result_handler != NULL) {
    _result_handler->data_comeback();         //對於同步呼叫,這個方法會喚醒客戶端使其從wait中返回                                                                       
  }                                                                                                                  
  
  delete[] buf;                                                                                                      
  //自殺 
  delete this;                                                                                                       
}

result_handler的呼叫是關鍵,我們正是利用這一點做到同步呼叫和非同步呼叫。ResultHandler的類UML如下:

DefaultResultHandler是SimpleRpc的預設結果處理方式,UserDefinedResultHandler由使用者自己選擇性的定義並實現。當客戶端工作執行緒對服務端相應資料處理完畢後,呼叫ResultHandler的data_comeback方法執行這個鉤子函式。

  • 同步呼叫的實現:
int SimpleRpcClient::sync_request(Server &server, Request *request, Response *response) {
  Mutex mutex;
  Connection conn;
  Condition cond(&mutex);
  InetAddr addr(server.get_port_str(), server.get_ip_str());  
  Connector conntor(addr);
  int ret = conntor.Connect(conn);  //建立與服務端的連線
  if(ret == -1){
    LOG("connect error\n");
    return -1;
  }
  int size = request->bytes();      //獲取請求序列化後的位元組數
  char *buf = new char[size + 4];   //用額外4位元組存放資料長度,方便接收端校驗
  if(buf == NULL) {
    LOG("request oom, request need %d bytes\n", size + 4);
    conn.Close();
    return -1;
  }
  int payload = request->serialize(buf + 4, size);  //序列化
  memcpy(buf, &payload, sizeof(int));
  ret = conn.send_n(buf, payload + 4);  //傳送序列化資料
  if(ret != 0) {
    LOG("connection send error\n");
    return -1;
  }
  DefaultResultHandler *handler = new DefaultResultHandler(&cond, &mutex);
  DownstreamHandler *down_handler =
    new DownstreamHandler(conn.sock(), response, Reactor::get_instance(), handler);
  Reactor::get_instance()->regist(conn.sock(), down_handler);  //註冊到reactor中等待響應事件的通知
  handler->finish();        //阻塞呼叫,直到cond得到喚醒通知
  delete[] buf;
  delete handler;
  return 0;
}

我們的DefautlResultHandler擁有系統等待條件(Condition),並且作為DownstreamHandler的成員之一。客戶端傳送請求資料後,構造DownstreamHandler並註冊到reactor中,等待服務端響應事件的通知。幹完以上的事情之後,客戶端應用執行緒呼叫DefaultResultHandler的finish方法阻塞直到得到完成通知,這樣達到了同步呼叫的效果。

  • 非同步呼叫的實現:

非同步呼叫沒有使用DefaultResultHandler作為引數傳遞給DownstreamHandler,而是把使用者自定義的ResultHanlder傳遞進去,具體的控制流程(data_comeback函式)由使用者自己定義。

int SimpleRpcClient::async_request(
  Server &server, Request *request, Response *response, ResultHandler *handler) {
  ...  
  DownstreamHandler *down_handler =
    new DownstreamHandler(conn.sock(), response, Reactor::get_instance(), handler);
  Reactor::get_instance()->regist(conn.sock(), down_handler);
  ...
}

 服務端工作執行緒計算模型


我們知道服務端使用多執行緒進行資料的處理,那麼每個執行緒的工作內容是什麼呢?

template<class REQUEST, class RESPONSE>                                                                                       
class Processor : public Worker<StreamEvent> {                                                                                
  public:                                                                                                                     
    virtual int process(REQUEST &request, RESPONSE &response) = 0;                                                            
                                                                                                                              
    void run() {                                                                                                              
      while(true){                                                                                                            
        StreamEvent e = get_event();      //佇列中獲取待處理事件                                                                                    
        char head[4];                                                                                                       
        Connection conn(e.fd);                                                                                              
        int payload = conn.recv_n(head, 4);     //接收資料長度                                                                            
        if(payload == -1) {                                                                                                 
          close(e.fd);                                                                                                      
          printf("Error Processor close fd:%d\n", e.fd);                                                                    
          return;                                                                                                           
        }                                                                                                                   
                                                                                                                              
        REQUEST request;                                                                                                    
        RESPONSE response;                                                                                                  
                                                                                                                              
        int size = *((int *)head);                                                                                          
        char *recv_buf = new char[size];                                                                                    
        conn.recv_n(recv_buf, size);              //接收請求資料                                                                         
        request.deserialize(recv_buf, size);      //反序列化                                                                          
                                                                                                                              
        process(request, response);               //進行使用者程式碼邏輯計算,由使用者實現                                                                            
        size = response.bytes();                                                                                            
        char *send_buf = new char[size + 4];                                                                                
        memcpy(send_buf, &size, sizeof(int));                                                                               
        payload = response.serialize(send_buf + 4, size);      //序列化響應資料                                                             
        conn.send_n(send_buf, size + 4);                       //傳送響應資料                                                                 
        //為了正常關閉該連結,需要重新註冊回reactor                                                                         
        UpstreamHandler *upHandler = new UpstreamHandler(e.fd, Reactor::get_instance());
        Reactor::get_instance()->regist(e.fd, upHandler);
        delete recv_buf;
        delete send_buf;
      }
    }
virutal ~Processor(){}
}

 

相關文章