Hadoop RPC客户端(Client)向RPC建立连接时向RPC服务器发送两部分内容:RPC Header和Header。RPC Header的格式如下:
private void writeRpcHeader(OutputStream outStream) throws IOException {
DataOutputStream out = new DataOutputStream(
new BufferedOutputStream(outStream));
// Write out the header, version and authentication method
out.write(Server.HEADER.array());
out.write(Server.CURRENT_VERSION);
authMethod.write(out);
out.flush();
}
其中Server.HEADER为ByteBuffer.wrap("hrpc".getBytes()),SERVER.CURRENT_VERSION为 4,最后为authMethod。
RPC Header发送完成后,Client会发送header部分,其具体内容如下:
private void writeHeader() throws IOException {
// Write out the ConnectionHeader
DataOutputBuffer buf = new DataOutputBuffer();
header.write(buf);
// Write out the payload length
int bufLen = buf.getLength();
out.writeInt(bufLen);
out.write(buf.getData(), 0, bufLen);
}
Header的主要包含三部分的内容:
class ConnectionHeader implements Writable {
private String protocol;
private UserGroupInformation ugi = null;
private AuthMethod authMethod;
......
}
RPC Header与Header在连接建立的时候发送并且只发送一次,在此之后,Client会发送每一次方法调用相关的信息:
Call call = new Call(param);
Connection connection = getConnection(remoteId, call);
connection.sendParam(call); // send the parameter
下面看一下Call的具体内容:
private class Call {
int id; // call id
Writable param; // parameter
......
}
其中id是本次方法调用的唯一标识,param是Invocation的一个实例,Invocation包含了三部分内容:要调用方法的名字、方法调用参数类型数组、参数数组。
private static class Invocation implements Writable, Configurable {
private String methodName;
private Class[] parameterClasses;
private Object[] parameters;
......
}
Call发送的具体代码如下:
d = new DataOutputBuffer();
d.writeInt(call.id);
call.param.write(d);
byte[] data = d.getData();
int dataLength = d.getLength();
out.writeInt(dataLength); // first put the data length
out.write(data, 0, dataLength);// write the data
out.flush();
综上所述,RPC协议的具体格式如下:
RPC Header | Header | Call | Call | ..... |
RPC Header:
‘hrpc’ | 4 | authMethod |
Header:
Header长度(Int) | ConnectionHeader |
Call
Call长度(INT) | Call id(int) | 方法名 | 参数类型数组 | 参数数组 |
下面我们看一下RPC服务器端是如何采用上面的协议进行交互的。按照设想,RPC服务器接受客户端的连接请求后,服务器首先读取RPC Header,再读取Header,最后不断的读取方法调用(Call)。服务器端的Socket读取都是通过Reader内部类最终由Server内部类Connection类来读取。为了顺利读取Server.Connection设置了几个内部变量,如下图所示。这里要特别说明一下dataLengthBuffer这个变量,因为这个变量的名字存在歧义。在第一次读取的时候(读取RPC Header的时候),dataLengthBuffer保存的是‘hrpc’的byte数组;在其他时候(读取Header的时候或者Call的时候)保存的是长度(int)。除此之外,还需要注意的是RPC采用的异步通信的模式,服务器采用如下的方式判断一次读取已经完成——如果读取的结果为-1或者buffer没有读满,则表明本次读取已经完成但是数据还没有完全读取完成,需要继续等待下一次读取。
boolean rpcHeaderRead = false; //判断RPCHeader是否已经读取完成
boolean headerRead = false; //判断Header是否已经读取完成
ByteBuffer data;//Header的数据或者每一次Call的数据
ByteBuffer dataLengthBuffer;//长度,Header的长度、Call的长度或者