RPC包格式:
这图是从别人那拿过来的 留着自己看,主要是对如下这段代码的理解有帮助:
/** * 请求报文: 4字节hrpc + 1字节Current_version + 1字节authMethod * 4字节Header_len + connection_header * 4字节Data_len + call.id + writable * * connection_header长度不定 call.id + writable长度不等 * * 返回报文: call.id + status.state + writable * errorClass + error * @return * @throws IOException * @throws InterruptedException */ public int readAndProcess() throws IOException, InterruptedException { while (true) { /* Read at most one RPC. If the header is not read completely yet * then iterate until we read first RPC or until there is no data left. */ int count = -1; //读取hrpc信息 if (dataLengthBuffer.remaining() > 0) { count = channelRead(channel, dataLengthBuffer); if (count < 0 || dataLengthBuffer.remaining() > 0) return count; } if (!rpcHeaderRead) {//一次连接 只会读一次 //Every connection is expected to send the header. if (rpcHeaderBuffer == null) { rpcHeaderBuffer = ByteBuffer.allocate(2); } count = channelRead(channel, rpcHeaderBuffer); if (count < 0 || rpcHeaderBuffer.remaining() > 0) { return count; } //读取current_version信息 int version = rpcHeaderBuffer.get(0); //读取authMethod(认证方法)信息 byte[] method = new byte[] {rpcHeaderBuffer.get(1)}; authMethod = AuthMethod.read(new DataInputStream(new ByteArrayInputStream(method))); dataLengthBuffer.flip(); if (!HEADER.equals(dataLengthBuffer) || version != CURRENT_VERSION) { //Warning is ok since this is not supposed to happen. LOG.warn("Incorrect header or version mismatch from " + hostAddress + ":" + remotePort + " got version " + version + " expected version " + CURRENT_VERSION); return -1; } dataLengthBuffer.clear(); if (authMethod == null) { throw new IOException("Unable to read authentication method"); } if (isSecurityEnabled && authMethod == AuthMethod.SIMPLE) { AccessControlException ae = new AccessControlException("Authentication is required"); setupResponse(authFailedResponse, authFailedCall, Status.FATAL, null, ae.getClass().getName(), ae.getMessage()); responder.doRespond(authFailedCall); throw ae; } if (!isSecurityEnabled && authMethod != AuthMethod.SIMPLE) { doSaslReply(SaslStatus.SUCCESS, new IntWritable(SaslRpcServer.SWITCH_TO_SIMPLE_AUTH), null, null); authMethod = AuthMethod.SIMPLE; // client has already sent the initial Sasl message and we // should ignore it. Both client and server should fall back // to simple auth from now on. skipInitialSaslHandshake = true; } if (authMethod != AuthMethod.SIMPLE) { useSasl = true; } rpcHeaderBuffer = null; rpcHeaderRead = true; continue; } //读取connection连接信息及真正的Data信息 if (data == null) { dataLengthBuffer.flip(); dataLength = dataLengthBuffer.getInt(); if (dataLength == Client.PING_CALL_ID) {//如果此RPC调用为ping则 直接返回 if(!useWrap) { //covers the !useSasl too dataLengthBuffer.clear(); return 0; //ping message } } if (dataLength < 0) { LOG.warn("Unexpected data length " + dataLength + "!! from " + getHostAddress()); } data = ByteBuffer.allocate(dataLength); } count = channelRead(channel, data); //当data没有空位置时 说明已经读完 if (data.remaining() == 0) { dataLengthBuffer.clear(); data.flip(); if (skipInitialSaslHandshake) { data = null; skipInitialSaslHandshake = false; continue; } boolean isHeaderRead = headerRead; if (useSasl) { saslReadAndProcess(data.array()); } else {//此处具体处理Data信息及Header信息 processOneRpc(data.array()); } data = null; if (!isHeaderRead) {//如果只读取了Header信息 继续循环读取Data信息 continue; } } return count; } }
private void processOneRpc(byte[] buf) throws IOException, InterruptedException { if (headerRead) {//读取Data信息 processData(buf); } else {//读取Header信息 processHeader(buf); headerRead = true; if (!authorizeConnection()) { throw new AccessControlException("Connection from " + this + " for protocol " + header.getProtocol() + " is unauthorized for user " + user); } } }
private void processData(byte[] buf) throws IOException, InterruptedException { DataInputStream dis = new DataInputStream(new ByteArrayInputStream(buf)); int id = dis.readInt(); // try to read an id if (LOG.isDebugEnabled()) LOG.debug(" got #" + id); Writable param = ReflectionUtils.newInstance(paramClass, conf);//read param param.readFields(dis); Call call = new Call(id, param, this); callQueue.put(call); // queue the call; maybe blocked here incRpcCount(); // Increment the rpc count }
/// Reads the connection header following version private void processHeader(byte[] buf) throws IOException { DataInputStream in = new DataInputStream(new ByteArrayInputStream(buf)); header.readFields(in); try { String protocolClassName = header.getProtocol(); if (protocolClassName != null) { protocol = getProtocolClass(header.getProtocol(), conf); } } catch (ClassNotFoundException cnfe) { throw new IOException("Unknown protocol: " + header.getProtocol()); } UserGroupInformation protocolUser = header.getUgi(); if (!useSasl) { user = protocolUser; if (user != null) { user.setAuthenticationMethod(AuthMethod.SIMPLE.authenticationMethod); } } else { // user is authenticated user.setAuthenticationMethod(authMethod.authenticationMethod); //Now we check if this is a proxy user case. If the protocol user is //different from the 'user', it is a proxy user scenario. However, //this is not allowed if user authenticated with DIGEST. if ((protocolUser != null) && (!protocolUser.getUserName().equals(user.getUserName()))) { if (authMethod == AuthMethod.DIGEST) { // Not allowed to doAs if token authentication is used throw new AccessControlException("Authenticated user (" + user + ") doesn't match what the client claims to be (" + protocolUser + ")"); } else { // Effective user can be different from authenticated user // for simple auth or kerberos auth // The user is the real user. Now we create a proxy user UserGroupInformation realUser = user; user = UserGroupInformation.createProxyUser(protocolUser .getUserName(), realUser); // Now the user is a proxy user, set Authentication method Proxy. user.setAuthenticationMethod(AuthenticationMethod.PROXY); } } } }
具体查看链接:http://fengshenwu.com/blog/2012/11/13/hadoop-rpc_develop/
我的hadoop源码(有一些我看代码时留下的注释):https://github.com/heipacker/hadoop-1.0.3-linux