Hadoop RPC機制

Hadoop RPC機制

轉載:http://www.iteye.com/topic/709993

1、心跳機制 


心跳的機制大概是這樣的: 
1) master啓動的時候,會開一個ipc server在那裏。 
2) slave啓動時,會連接master,並每隔3秒鐘主動向master發送一個“心跳”,將自己的狀態信息告訴master,然後master也是通過這個心跳的返回值,向slave節點傳達指令。 


2、找到心跳的代碼 

拿namenode和datanode來說,在datanode的offerService方法中,每隔3秒向namenode發送心跳的代碼: 

Java代碼  收藏代碼
  1. /** 
  2.   * Main loop for the DataNode.  Runs until shutdown, 
  3.   * forever calling remote NameNode functions. 
  4.   */  
  5.  public void offerService() throws Exception {  
  6.       
  7.    ...  
  8.   
  9.    //  
  10.    // Now loop for a long time....  
  11.    //  
  12.   
  13.    while (shouldRun) {  
  14.      try {  
  15.        long startTime = now();  
  16.   
  17.        //  
  18.        // Every so often, send heartbeat or block-report  
  19.        //  
  20.          
  21. // 如果到了3秒鐘,就向namenode發心跳  
  22.        if (startTime - lastHeartbeat > heartBeatInterval) {  
  23.          //  
  24.          // All heartbeat messages include following info:  
  25.          // -- Datanode name  
  26.          // -- data transfer port  
  27.          // -- Total capacity  
  28.          // -- Bytes remaining  
  29.          //  
  30.          lastHeartbeat = startTime;  
  31.          DatanodeCommand[] cmds = namenode.sendHeartbeat(dnRegistration,  
  32.                                                       data.getCapacity(),  
  33.                                                       data.getDfsUsed(),  
  34.                                                       data.getRemaining(),  
  35.                                                       xmitsInProgress.get(),  
  36.                                                       getXceiverCount());  
  37.   
  38.   // 注意上面這行代碼,“發送心跳”竟然就是調用namenode的一個方法??  
  39.   
  40.          myMetrics.heartbeats.inc(now() - startTime);  
  41.          //LOG.info("Just sent heartbeat, with name " + localName);  
  42.   
  43.   // 處理對心跳的返回值(namenode傳給datanode的指令)  
  44.          if (!processCommand(cmds))  
  45.            continue;  
  46.        }  
  47.   
  48.     // 這裏省略很多代碼  
  49. ...  
  50.    } // while (shouldRun)  
  51.  } // offerService  

上面這段代碼,如果是單機的程序,沒什麼值得奇怪的。但是,這是hadoop集羣!datanode和namenode在2臺不同的機器(或2個JVM)上運行!datanode機器竟然直接調用namenode的方法!這是怎麼實現的?難道是傳說中的RMI嗎?? 

下面我們主要就來分析這個方法調用的細節。 


3、心跳的底層細節一:datanode怎麼獲得namenode對象的? 

首先,DataNode類中,有一個namenode的成員變量: 
Java代碼  收藏代碼
  1. public class DataNode extends Configured   
  2.     implements InterDatanodeProtocol, ClientDatanodeProtocol, FSConstants, Runnable {  
  3.   ...  
  4.   public DatanodeProtocol namenode = null;  
  5.   ...   
  6. }  

下面是NameNode類的定義: 
Java代碼  收藏代碼
  1. public class NameNode implements ClientProtocol, DatanodeProtocol,  
  2.                                  NamenodeProtocol, FSConstants,  
  3.                                  RefreshAuthorizationPolicyProtocol {  
  4.   ...   
  5. }  


注意:NameNode實現了DatanodeProtocol接口,DatanodeProtocol接口定義了namenode和datanode之間通信的方法。 

那麼,DataNode類是怎麼獲取到NameNode類的引用呢? 

在Datanode端,爲namenode變量賦值的代碼: 
Java代碼  收藏代碼
  1. // connect to name node  
  2. this.namenode = (DatanodeProtocol)   
  3.   RPC.waitForProxy(DatanodeProtocol.class,  
  4.                    DatanodeProtocol.versionID,  
  5.                    nameNodeAddr,   
  6.                    conf);  


在繼續去RPC類中追蹤: 
Java代碼  收藏代碼
  1. VersionedProtocol proxy =  
  2.         (VersionedProtocol) Proxy.newProxyInstance(  
  3.             protocol.getClassLoader(), new Class[] { protocol },  
  4.             new Invoker(addr, ticket, conf, factory));  


現在,明白了! 
1) 對namenode的賦值,並不是真正的new了一個實現了DatanodeProtocol接口的對象,而是獲得了一個動態代理!! 
2) 上面這段代碼中,protocol的類型是DatanodeProtocol.class 
3) 對namenode的所有調用,都被委託(delegate)給了Invoker 


4、心跳的底層細節二:看看Invoker類 

Invoker類是org.apache.hadoop.ipc.RPC類的一個靜態內部類: 

Java代碼  收藏代碼
  1. private static class Invoker implements InvocationHandler {  
在這個類中,看invoke方法: 
  
Java代碼  收藏代碼
  1. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
  2.             ...  
  3.   
  4.               ObjectWritable value = (ObjectWritable)  
  5.                 client.call(new Invocation(method, args), address,   
  6.                             method.getDeclaringClass(), ticket);  
  7.                 ...  
  8.               return value.get();  
  9.            }  

所有的方法調用又被delegate給client的call方法了! 

client是Invoker中的成員變量: 
  
Java代碼  收藏代碼
  1. private Client client;  

所以可以看出:DatanodeProtocol中的每個方法調用,都被包裝成一個Invocation對象,再由client.call()調用 


5、心跳的底層細節三:Invocation類 

Invocation類是org.apache.hadoop.ipc.RPC類的一個靜態內部類 

沒有什麼業務邏輯方法,主要作用就是一個VO 


6、心跳的底層細節四:client類的call方法 

接下來重點看client類的call方法: 
Java代碼  收藏代碼
  1. public Writable call(Writable param, InetSocketAddress addr,   
  2.                      Class<?> protocol, UserGroupInformation ticket)    
  3.                      throws InterruptedException, IOException {  
  4.   
  5.   Call call = new Call(param);     
  6. // 將Invocation轉化爲Call  
  7.   Connection connection = getConnection(addr, protocol, ticket, call);  
  8. // 連接遠程服務器  
  9.   connection.sendParam(call);                 // send the parameter  
  10. // 將“序列化”後的call發給過去  
  11.   boolean interrupted = false;  
  12.   synchronized (call) {  
  13.     while (!call.done) {  
  14.       try {  
  15.         call.wait();                           // wait for the result  
  16. // 等待調用結果  
  17.       } catch (InterruptedException ie) {  
  18.         // save the fact that we were interrupted  
  19.         interrupted = true;  
  20.       }  
  21.     }  
  22.   
  23.     if (interrupted) {  
  24.       // set the interrupt flag now that we are done waiting  
  25.       Thread.currentThread().interrupt();  
  26.     }  
  27.   
  28.     if (call.error != null) {  
  29.       if (call.error instanceof RemoteException) {  
  30.         call.error.fillInStackTrace();  
  31.         throw call.error;  
  32.       } else { // local exception  
  33.         throw wrapException(addr, call.error);  
  34.       }  
  35.     } else {  
  36.       return call.value;  
  37. // 返回  
  38.     }  
  39.   }  
  40. }  



7、現在,一目瞭然了 

Java代碼  收藏代碼
  1. datanode向namenode發送heartbeat過程是這樣的:  
  2.   
  3.     a) 在datanode初始化獲得namenode的proxy  
  4.     b) 在datanode上,調用namenode proxy的heartbeat方法:  
  5.         namenode.sendHeartbeat(dnRegistration,  
  6.                                                        data.getCapacity(),  
  7.                                                        data.getDfsUsed(),  
  8.                                                        data.getRemaining(),  
  9.                                                        xmitsInProgress.get(),  
  10.                                                        getXceiverCount());  
  11.     c) 在datanode上的namenode動態代理類將這個調用包裝成(或者叫“序列化成”)一個Invocation對象,並調用client.call方法  
  12.     d) client call方法將Invocation轉化爲Call對象  
  13.     e) client 將call發送到真正的namenode服務器  
  14.     f) namenode接收後,轉化成namenode端的Call,並process後,通過Responder發回來!  
  15.     g) datanode接收結果,並將結果轉化爲DatanodeCommand[]  
  16.           



8、再看動態代理 

動態代理:讓“只有接口,沒事對應的實現類”成爲可能,因爲具體方法的實現可以委託給另一個類!! 

在這個例子中,就datanode而言,DatanodeProtocol接口是沒有實現類的! 



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章