protected synchronized void setupConnection() throws IOException { short ioFailures = 0; short timeoutFailures = 0; while (true) { try { this.socket = socketFactory.createSocket(); this.socket.setTcpNoDelay(tcpNoDelay); this.socket.setKeepAlive(tcpKeepAlive); // connection time out is 20s NetUtils.connect(this.socket, remoteId.getAddress(), getSocketTimeout(conf)); if (remoteId.rpcTimeout > 0) { pingInterval = remoteId.rpcTimeout; // overwrite pingInterval } this.socket.setSoTimeout(pingInterval); return; } catch (SocketTimeoutException toe) { /* The max number of retries is 45, * which amounts to 20s*45 = 15 minutes retries. */ handleConnectionFailure(timeoutFailures++, maxRetries, toe); } catch (IOException ie) { handleConnectionFailure(ioFailures++, maxRetries, ie); } } }
/** * Retrieve a BlockReader suitable for reading. This method will reuse the cached connection to * the DN if appropriate. Otherwise, it will create a new connection. * * @param dnAddr Address of the datanode * @param chosenNode Chosen datanode information * @param file File location * @param block The Block object * @param blockToken The access token for security * @param startOffset The read offset, relative to block head * @param len The number of bytes to read * @param bufferSize The IO buffer size (not the client buffer size) * @param verifyChecksum Whether to verify checksum * @param clientName Client name * @return New BlockReader instance */ protected BlockReader getBlockReader( InetSocketAddress dnAddr, DatanodeInfo chosenNode, String file, ExtendedBlock block, Token<BlockTokenIdentifier> blockToken, long startOffset, long len, int bufferSize, boolean verifyChecksum, String clientName) throws IOException { if (dfsClient.shouldTryShortCircuitRead(dnAddr)) { return DFSClient.getLocalBlockReader( dfsClient.conf, src, block, blockToken, chosenNode, dfsClient.hdfsTimeout, startOffset); } IOException err = null; boolean fromCache = true; // Allow retry since there is no way of knowing whether the cached socket // is good until we actually use it. for (int retries = 0; retries <= nCachedConnRetry && fromCache; ++retries) { Socket sock = null; // Don't use the cache on the last attempt - it's possible that there // are arbitrarily many unusable sockets in the cache, but we don't // want to fail the read. if (retries < nCachedConnRetry) { sock = socketCache.get(dnAddr); } if (sock == null) { fromCache = false; sock = dfsClient.socketFactory.createSocket(); // TCP_NODELAY is crucial here because of bad interactions between // Nagle's Algorithm and Delayed ACKs. With connection keepalive // between the client and DN, the conversation looks like: // 1. Client -> DN: Read block X // 2. DN -> Client: data for block X // 3. Client -> DN: Status OK (successful read) // 4. Client -> DN: Read block Y // The fact that step #3 and #4 are both in the client->DN direction // triggers Nagling. If the DN is using delayed ACKs, this results // in a delay of 40ms or more. // // TCP_NODELAY disables nagling and thus avoids this performance // disaster. sock.setTcpNoDelay(true); NetUtils.connect( sock, dnAddr, dfsClient.getRandomLocalInterfaceAddr(), dfsClient.getConf().socketTimeout); sock.setSoTimeout(dfsClient.getConf().socketTimeout); } try { // The OP_READ_BLOCK request is sent as we make the BlockReader BlockReader reader = BlockReaderFactory.newBlockReader( dfsClient.getConf(), sock, file, block, blockToken, startOffset, len, bufferSize, verifyChecksum, clientName); return reader; } catch (IOException ex) { // Our socket is no good. DFSClient.LOG.debug("Error making BlockReader. Closing stale " + sock, ex); sock.close(); err = ex; } } throw err; }