/** Reads map entries from a socket, this could be a client or server socket */ private class TcpSocketChannelEntryReader { private final ByteBuffer in; private final ByteBufferBytes out; // we use Integer.MIN_VALUE as N/A private int sizeOfNextEntry = Integer.MIN_VALUE; public long lastHeartBeatReceived = System.currentTimeMillis(); private TcpSocketChannelEntryReader() { in = ByteBuffer.allocateDirect(packetSize + maxEntrySizeBytes); out = new ByteBufferBytes(in); out.limit(0); in.clear(); } /** * reads from the socket and writes them to the buffer * * @param socketChannel the socketChannel to read from * @return the number of bytes read * @throws IOException */ private int readSocketToBuffer(@NotNull final SocketChannel socketChannel) throws IOException { compactBuffer(); final int len = socketChannel.read(in); out.limit(in.position()); return len; } /** * reads entries from the buffer till empty * * @throws InterruptedException */ private void entriesFromBuffer() throws InterruptedException, IOException { for (; ; ) { out.limit(in.position()); // its set to MIN_VALUE when it should be read again if (sizeOfNextEntry == Integer.MIN_VALUE) { if (out.remaining() < SIZE_OF_SHORT) { return; } int value = out.readUnsignedShort(); // this is the heartbeat if (value == 0) continue; sizeOfNextEntry = value; } if (out.remaining() < sizeOfNextEntry) { return; } final long nextEntryPos = out.position() + sizeOfNextEntry; final long limit = out.limit(); out.limit(nextEntryPos); externalizable.readExternalEntry(out); out.limit(limit); // skip onto the next entry out.position(nextEntryPos); // to allow the sizeOfNextEntry to be read the next time around sizeOfNextEntry = Integer.MIN_VALUE; } } /** compacts the buffer and updates the {@code in} and {@code out} accordingly */ private void compactBuffer() { // the maxEntrySizeBytes used here may not be the maximum size of the entry in its serialized // form // however, its only use as an indication that the buffer is becoming full and should be // compacted // the buffer can be compacted at any time if (in.position() == 0 || in.remaining() > maxEntrySizeBytes) return; in.limit(in.position()); in.position((int) out.position()); in.compact(); out.position(0); } /** @return the identifier or -1 if unsuccessful */ byte identifierFromBuffer() { return (out.remaining() >= 1) ? out.readByte() : Byte.MIN_VALUE; } /** @return the timestamp or -1 if unsuccessful */ long remoteBootstrapTimestamp() { return (out.remaining() >= 8) ? out.readLong() : Long.MIN_VALUE; } public long remoteHeartbeatIntervalFromBuffer() { return (out.remaining() >= 8) ? out.readLong() : Long.MIN_VALUE; } }
@Override void process() throws IOException { try { final Details serverDetails = new Details(serverInetSocketAddress, localIdentifier); connectorBySocket.put(serverInetSocketAddress, new ServerConnector(serverDetails)); for (InetSocketAddress client : endpoints) { final Details clientDetails = new Details(client, localIdentifier); connectorBySocket.put(client, new ClientConnector(clientDetails)); } for (AbstractConnector connector : connectorBySocket.values()) { connector.connect(); } while (selector.isOpen()) { registerPendingRegistrations(); final int nSelectedKeys = selector.select(selectorTimeout); // its less resource intensive to set this less frequently and use an approximation final long approxTime = System.currentTimeMillis(); checkThrottleInterval(); // check that we have sent and received heartbeats heartBeatMonitor(approxTime); // set the OP_WRITE when data is ready to send opWriteUpdater.applyUpdates(); if (nSelectedKeys == 0) continue; // go back and check pendingRegistrations final Set<SelectionKey> selectionKeys = selector.selectedKeys(); for (final SelectionKey key : selectionKeys) { try { if (!key.isValid()) continue; if (key.isAcceptable()) onAccept(key); if (key.isConnectable()) onConnect(key); if (key.isReadable()) onRead(key, approxTime); if (key.isWritable()) onWrite(key, approxTime); } catch (CancelledKeyException e) { quietClose(key, e); } catch (ClosedSelectorException e) { quietClose(key, e); } catch (IOException e) { quietClose(key, e); } catch (InterruptedException e) { quietClose(key, e); } catch (Exception e) { LOG.info("", e); closeEarlyAndQuietly(key.channel()); } } selectionKeys.clear(); } } catch (CancelledKeyException e) { if (LOG.isDebugEnabled()) LOG.debug("", e); } catch (ClosedSelectorException e) { if (LOG.isDebugEnabled()) LOG.debug("", e); } catch (ClosedChannelException e) { if (LOG.isDebugEnabled()) LOG.debug("", e); } catch (ConnectException e) { if (LOG.isDebugEnabled()) LOG.debug("", e); } catch (Exception e) { LOG.error("", e); } finally { if (selector != null) try { selector.close(); } catch (IOException e) { if (LOG.isDebugEnabled()) LOG.debug("", e); } close(); } }