Hadoop学习八:Hadoop-Hdfs RPC源码 Client

Hadoop学习八:Hadoop-Hdfs RPC源码 Client

    博客分类: 
  • Hadoop
个人笔记:
从该文章中可以看出,整个RPC的设计核心在于Connection。其中它的设计思路是这样的:
      1.有JAVA最基本的Socket,InetSocketAddress网络接口。
2.建立自己的IO流:DataInputStream,DataOutputStream
3.引入自己的封装类: ConnectionId,ConnectionHeader
      4.对外提供getIOStream的方法来建立与服务端的连接:建立socket连接----把socket get出来的IO流封装到自己的流中---发送PRC报文---发送Header--启动自己的线程运作。

一.Client类图

 Hadoop学习八:Hadoop-Hdfs RPC源码 Client_第1张图片

二.详细描述

  1.  ConnectionId:This class holds the address and the user ticket. The client connections to servers are uniquely identified by <remoteAddress, protocol, ticket>。一个connection由一个ConnectionId唯一标识;所以要重写ConnectionId的equals和hashcode方法。
  2. ConnectionHeader:The IPC connection header sent by the client to the server on connection establishment.
  3. Connection:继承Thread。代表client到server的一个连接。我在文中将Connection对象称为“连接”。
  4. Java代码   收藏代码
    1.   /** Thread that reads responses and notifies callers.  Each connection owns a 
    2.    * socket connected to a remote address.  Calls are multiplexed through this 
    3.    * socket: responses may be delivered out of order. */  
    4.   private class Connection extends Thread {  
    5.      //一个连接的基本信息  
    6.     private InetSocketAddress server;             // server ip:port,注意是服务端  
    7.     private ConnectionHeader header;              // connection header  
    8.     private final ConnectionId remoteId;                // connection id  
    9.     private Socket socket = null;                 // connected socket  
    10.     private DataInputStream in;  
    11.     private DataOutputStream out;  
    12.       
    13.     //所有的调用  
    14.     private Hashtable<Integer, Call> calls = new Hashtable<Integer, Call>();  
    15.       
    16.     //1.构造函数只初始化一些基本信息  
    17.     public Connection(ConnectionId remoteId) throws IOException {  
    18.       this.remoteId = remoteId;  
    19.       this.server = remoteId.getAddress();  
    20.       header = new ConnectionHeader(protocol == null ? null : protocol.getName(), ticket, authMethod);  
    21.       this.setName("IPC Client (" + socketFactory.hashCode() +") connection to " +  
    22.           remoteId.getAddress().toString() +" from " + ((ticket==null)?"an unknown user":ticket.getUserName()));  
    23.       this.setDaemon(true);  
    24.     }  
    25.   
    26.     //2.与IPC server建立socket连接  
    27.     private synchronized void setupConnection() throws IOException {  
    28.       while (true) {  
    29.         try {  
    30.           this.socket = socketFactory.createSocket();  
    31.       }  
    32.     }  
    33.     /** Connect to the server and set up the I/O streams. It then sends 
    34.      * a header to the server and starts 
    35.      * the connection thread that waits for responses. 
    36.      */  
    37.     private synchronized void setupIOstreams() throws InterruptedException {  
    38.       try {  
    39.         while (true) {  
    40.           //2.与IPC server建立socket连接  
    41.           setupConnection();  
    42.           //3.创建流  
    43.           InputStream inStream = NetUtils.getInputStream(socket);  
    44.           OutputStream outStream = NetUtils.getOutputStream(socket);  
    45.           //4.发送RPC报头  
    46.           writeRpcHeader(outStream);  
    47.           this.in = new DataInputStream(new BufferedInputStream (new PingInputStream(inStream)));  
    48.           this.out = new DataOutputStream(new BufferedOutputStream(outStream));  
    49.           //5.发送connection header到server  
    50.           writeHeader();  
    51.           //6.启动自己(线程),接受response  
    52.           start();  
    53.           return;  
    54.         }  
    55.       } catch (Throwable t) {  
    56.         if (t instanceof IOException) {  
    57.           markClosed((IOException)t);  
    58.         } else {  
    59.           markClosed(new IOException("Couldn't set up IO streams", t));  
    60.         }  
    61.         close();  
    62.       }  
    63.     }  
    64.   
    65.     //4.发送RPC报头  
    66.     private void writeRpcHeader(OutputStream outStream) throws IOException {  
    67.       DataOutputStream out = new DataOutputStream(new BufferedOutputStream(outStream));  
    68.       //public static final ByteBuffer HEADER = ByteBuffer.wrap("hrpc".getBytes());  
    69.       //public static final byte CURRENT_VERSION = 4;  
    70.       out.write(Server.HEADER.array());  
    71.       out.write(Server.CURRENT_VERSION);  
    72.       authMethod.write(out);  
    73.       out.flush();  
    74.     }  
    75.       
    76.     //5.发送connection header到server  
    77.     private void writeHeader() throws IOException {  
    78.       DataOutputBuffer buf = new DataOutputBuffer();  
    79.       header.write(buf);  
    80.         
    81.       int bufLen = buf.getLength();  
    82.       out.writeInt(bufLen);  
    83.       out.write(buf.getData(), 0, bufLen);  
    84.     }  
    85.       
    86.     //6.启动自己(线程),等待接受response,接受完后关闭此连接  
    87.     public void run() {  
    88.       while (waitForWork()) {//Return true if it is time to read a response; false otherwise.  
    89.         receiveResponse();  
    90.       }  
    91.       //关闭此连接  
    92.       close();  
    93.     }  
    94.   
    95.     //7.发送请求:长度+内容  
    96.     public void sendParam(Call call) {  
    97.       DataOutputBuffer d=null;  
    98.         synchronized (this.out) {  
    99.           d = new DataOutputBuffer();  
    100.           d.writeInt(call.id);  
    101.           call.param.write(d);  
    102.           byte[] data = d.getData();  
    103.           int dataLength = d.getLength();  
    104.           out.writeInt(dataLength);      //first put the data length  
    105.           out.write(data, 0, dataLength);//write the data  
    106.           out.flush();  
    107.         }  
    108.     }    
    109.   
    110.     //6.接受response,把结果赋值给Call对象  
    111.     private void receiveResponse() {  
    112.         int id = in.readInt();                    // try to read an id  
    113.         Call call = calls.get(id);  
    114.         int state = in.readInt();     // read call status  
    115.         if (state == Status.SUCCESS.state) {  
    116.           Writable value = ReflectionUtils.newInstance(valueClass, conf);  
    117.           value.readFields(in);                 // read value  
    118.           call.setValue(value);  
    119.           calls.remove(id);  
    120.         }   
    121.     }  
    122.       
    123. }  
  5. Call对象:RPC是基于反射的,每次方法调用都对应一个Call对象,我在文中将Call对象称为“调用”。
    Java代码   收藏代码
    1. private class Call {  
    2.     int id;                                       // call id,唯一标示一个Call  
    3.     Writable param;                        // parameter,创建Call对象时赋值  
    4.     Writable value;                          // value, Connecion线程接受到server的response后赋值给value  
    5.     boolean done;                           // true when call is done  
    6.   
    7.     protected Call(Writable param) {  
    8.       this.param = param;  
    9.       synchronized (Client.this) {  
    10.         this.id = counter++;  
    11.       }  
    12.     }  
    13. }  
  6. ParallelCall:继承Call,还是一个Call对象,只是这些Call对象共享一个ParallelResults。
    Java代码   收藏代码
    1. private class ParallelCall extends Call {  
    2.     private ParallelResults results; //多个Call共享一个ParallelResults  
    3.     private int index;  
    4.       
    5.     public ParallelCall(Writable param, ParallelResults results, int index) {  
    6.       super(param);  
    7.       this.results = results;  
    8.       this.index = index;  
    9.     }  
    10.   
    11.     /** Deliver result to result collector. */  
    12.     protected void callComplete() {  
    13.       results.callComplete(this);  
    14.     }  
    15.   }  
     
  7. ParallelResults:一组Call对象的返回结果。
    Java代码   收藏代码
    1. private static class ParallelResults {  
    2.             private Writable[] values;  
    3.             private int size;   //一共有多少个Call要返回  
    4.             private int count;  //实际已经返回几个  
    5.   
    6.             public ParallelResults(int size) {  
    7.               this.values = new Writable[size];  
    8.               this.size = size;  
    9.             }  
    10.   
    11.             /** Collect a result. */  
    12.             public synchronized void callComplete(ParallelCall call) {  
    13.               values[call.index] = call.value;            // store the value  
    14.               count++;                                    // count it  
    15.               if (count == size)                          // if all values are in  
    16.                 notify();                                 // then notify waiting caller  
    17.             }  
    18.           }   
  8. Client:IPC client端。调用client的call方法,传入Writable作为参数,返回一个Writable作为结果。
    Java代码   收藏代码
    1. /** A client for an IPC service.  IPC calls take a single {@link Writable} as a 
    2.  * parameter, and return a {@link Writable} as their value.  A service runs on 
    3.  * a port and is defined by a parameter class and a value class. 
    4.  *  
    5.  * @see Server 
    6.  */  
    7. public class Client {  
    8.   //缓存client到server的所有连接  
    9.   private Hashtable<ConnectionId, Connection> connections = new Hashtable<ConnectionId, Connection>();  
    10.   
    11.   private Class<? extends Writable> valueClass;   //Call对象value的类型  
    12.   private int counter;                            //创建一个Call的时候,用counter++作为Call的id  
    13.   final private Configuration conf;  
    14.   
    15.   private SocketFactory socketFactory;           //服务器端的ip+port创建的socketFactory  
    16.     
    17.   //1.初始化Client  
    18.   public Client(Class<? extends Writable> valueClass, Configuration conf,   
    19.       SocketFactory factory) {  
    20.     this.valueClass = valueClass;  
    21.     this.conf = conf;  
    22.     this.socketFactory = factory; //初始化client时传入  
    23.   }  
    24.   
    25.   //2.用client发送一个请求  
    26.   public Writable call(Writable param, ConnectionId remoteId)    
    27.                        throws InterruptedException, IOException {  
    28.       //创建Call对象  
    29.     Call call = new Call(param);  
    30.     //创建Connection对象  
    31.     Connection connection = getConnection(remoteId, call);  
    32.     //发送请求,参考Connection类代码7  
    33.     connection.sendParam(call);                   
    34.     ...  
    35.     return call.value;  
    36.   }  
    37.   
    38.   //2.用client一次发送多个请求  
    39.   public Writable[] call(Writable[] params, InetSocketAddress[] addresses,  
    40.       Class<?> protocol, UserGroupInformation ticket, Configuration conf)  
    41.       throws IOException, InterruptedException {  
    42.      //创建一个结果集  
    43.     ParallelResults results = new ParallelResults(params.length);  
    44.     synchronized (results) {  
    45.       for (int i = 0; i < params.length; i++) {  
    46.          //创建每个Call  
    47.         ParallelCall call = new ParallelCall(params[i], results, i);  
    48.         try {  
    49.          //创建每个Connection对象,不同的Call存放到不同的连接上(Each parameter is sent to the corresponding address.)。  
    50.           ConnectionId remoteId = ConnectionId.getConnectionId(addresses[i],  
    51.               protocol, ticket, 0, conf);  
    52.           Connection connection = getConnection(remoteId, call);  
    53.           //发送请求,参考Connection类代码7  
    54.           connection.sendParam(call);              
    55.         } catch (IOException e) {  
    56.         }  
    57.       }  
    58.       while (results.count != results.size) {  
    59.         try {  
    60.           results.wait();                    // wait for all results  
    61.         } catch (InterruptedException e) {}  
    62.       }  
    63.       //放回所有结果  
    64.       return results.values;  
    65.     }  
    66.   }  
    67.   
    68.   /** Get a connection from the pool, or create a new one and add it to the 
    69.    * pool.  Connections to a given ConnectionId are reused. */  
    70.   //获得一个连接,首先从缓存中去;取不到,创建一个,并放到缓存中。  
    71.    private Connection getConnection(ConnectionId remoteId, Call call) throws IOException, InterruptedException {  
    72.     Connection connection;  
    73.     do {  
    74.       synchronized (connections) {  
    75.         connection = connections.get(remoteId);  
    76.         if (connection == null) {  
    77.          //参考Connection类代码1  
    78.           connection = new Connection(remoteId);  
    79.           connections.put(remoteId, connection);  
    80.         }  
    81.       }  
    82.     } while (!connection.addCall(call));//往创建的连接里加入call  
    83.     //参考Connection类代码23456  
    84.     connection.setupIOstreams();  
    85.     return connection;  
    86.   }  
    87.   
    88. }  

你可能感兴趣的:(Hadoop学习八:Hadoop-Hdfs RPC源码 Client)