Search in sources :

Example 11 with PeerDescription

use of org.apache.nifi.remote.PeerDescription in project nifi by apache.

the class EndpointConnectionPool method getEndpointConnection.

public EndpointConnection getEndpointConnection(final TransferDirection direction, final SiteToSiteClientConfig config) throws IOException {
    // 
    // Attempt to get a connection state that already exists for this URL.
    // 
    FlowFileCodec codec = null;
    CommunicationsSession commsSession = null;
    SocketClientProtocol protocol = null;
    EndpointConnection connection;
    Peer peer = null;
    final URI clusterUrl;
    try {
        clusterUrl = siteInfoProvider.getActiveClusterUrl();
    } catch (final IOException ioe) {
        throw new UnreachableClusterException("Unable to refresh details from any of the configured remote instances.", ioe);
    }
    do {
        final List<EndpointConnection> addBack = new ArrayList<>();
        logger.debug("{} getting next peer status", this);
        final PeerStatus peerStatus = peerSelector.getNextPeerStatus(direction);
        logger.debug("{} next peer status = {}", this, peerStatus);
        if (peerStatus == null) {
            return null;
        }
        final PeerDescription peerDescription = peerStatus.getPeerDescription();
        BlockingQueue<EndpointConnection> connectionQueue = connectionQueueMap.get(peerDescription);
        if (connectionQueue == null) {
            connectionQueue = new LinkedBlockingQueue<>();
            BlockingQueue<EndpointConnection> existing = connectionQueueMap.putIfAbsent(peerDescription, connectionQueue);
            if (existing != null) {
                connectionQueue = existing;
            }
        }
        try {
            connection = connectionQueue.poll();
            logger.debug("{} Connection State for {} = {}", this, clusterUrl, connection);
            final String portId = getPortIdentifier(direction);
            if (connection == null && !addBack.isEmpty()) {
                // all available connections have been penalized.
                logger.debug("{} all Connections for {} are penalized; returning no Connection", this, portId);
                return null;
            }
            if (connection != null && connection.getPeer().isPenalized(portId)) {
                // we have a connection, but it's penalized. We want to add it back to the queue
                // when we've found one to use.
                addBack.add(connection);
                continue;
            }
            // if we can't get an existing Connection, create one
            if (connection == null) {
                logger.debug("{} No Connection available for Port {}; creating new Connection", this, portId);
                protocol = new SocketClientProtocol();
                protocol.setDestination(new IdEnrichedRemoteDestination(remoteDestination, portId));
                protocol.setEventReporter(eventReporter);
                final long penalizationMillis = remoteDestination.getYieldPeriod(TimeUnit.MILLISECONDS);
                try {
                    logger.debug("{} Establishing site-to-site connection with {}", this, peerStatus);
                    commsSession = establishSiteToSiteConnection(peerStatus);
                } catch (final IOException ioe) {
                    peerSelector.penalize(peerStatus.getPeerDescription(), penalizationMillis);
                    throw ioe;
                }
                final DataInputStream dis = new DataInputStream(commsSession.getInput().getInputStream());
                final DataOutputStream dos = new DataOutputStream(commsSession.getOutput().getOutputStream());
                try {
                    logger.debug("{} Negotiating protocol", this);
                    RemoteResourceInitiator.initiateResourceNegotiation(protocol, dis, dos);
                } catch (final HandshakeException e) {
                    try {
                        commsSession.close();
                    } catch (final IOException ioe) {
                        throw e;
                    }
                }
                final String peerUrl = "nifi://" + peerDescription.getHostname() + ":" + peerDescription.getPort();
                peer = new Peer(peerDescription, commsSession, peerUrl, clusterUrl.toString());
                // set properties based on config
                if (config != null) {
                    protocol.setTimeout((int) config.getTimeout(TimeUnit.MILLISECONDS));
                    protocol.setPreferredBatchCount(config.getPreferredBatchCount());
                    protocol.setPreferredBatchSize(config.getPreferredBatchSize());
                    protocol.setPreferredBatchDuration(config.getPreferredBatchDuration(TimeUnit.MILLISECONDS));
                }
                // perform handshake
                try {
                    logger.debug("{} performing handshake", this);
                    protocol.handshake(peer);
                    // handle error cases
                    if (protocol.isDestinationFull()) {
                        logger.warn("{} {} indicates that port {}'s destination is full; penalizing peer", this, peer, config.getPortName() == null ? config.getPortIdentifier() : config.getPortName());
                        peerSelector.penalize(peer, penalizationMillis);
                        try {
                            peer.close();
                        } catch (final IOException ioe) {
                        }
                        continue;
                    } else if (protocol.isPortInvalid()) {
                        peerSelector.penalize(peer, penalizationMillis);
                        cleanup(protocol, peer);
                        throw new PortNotRunningException(peer.toString() + " indicates that port " + portId + " is not running");
                    } else if (protocol.isPortUnknown()) {
                        peerSelector.penalize(peer, penalizationMillis);
                        cleanup(protocol, peer);
                        throw new UnknownPortException(peer.toString() + " indicates that port " + portId + " is not known");
                    }
                    // negotiate the FlowFileCodec to use
                    logger.debug("{} negotiating codec", this);
                    codec = protocol.negotiateCodec(peer);
                    logger.debug("{} negotiated codec is {}", this, codec);
                } catch (final PortNotRunningException | UnknownPortException e) {
                    throw e;
                } catch (final Exception e) {
                    peerSelector.penalize(peer, penalizationMillis);
                    cleanup(protocol, peer);
                    final String message = String.format("%s failed to communicate with %s due to %s", this, peer == null ? clusterUrl : peer, e.toString());
                    error(logger, eventReporter, message);
                    if (logger.isDebugEnabled()) {
                        logger.error("", e);
                    }
                    throw e;
                }
                connection = new EndpointConnection(peer, protocol, codec);
            } else {
                final long lastTimeUsed = connection.getLastTimeUsed();
                final long millisSinceLastUse = System.currentTimeMillis() - lastTimeUsed;
                if (commsTimeout > 0L && millisSinceLastUse >= commsTimeout) {
                    cleanup(connection.getSocketClientProtocol(), connection.getPeer());
                    connection = null;
                } else {
                    codec = connection.getCodec();
                    peer = connection.getPeer();
                    commsSession = peer.getCommunicationsSession();
                    protocol = connection.getSocketClientProtocol();
                }
            }
        } catch (final Throwable t) {
            if (commsSession != null) {
                try {
                    commsSession.close();
                } catch (final IOException ioe) {
                }
            }
            throw t;
        } finally {
            if (!addBack.isEmpty()) {
                connectionQueue.addAll(addBack);
                addBack.clear();
            }
        }
    } while (connection == null || codec == null || commsSession == null || protocol == null);
    activeConnections.add(connection);
    return connection;
}
Also used : DataOutputStream(java.io.DataOutputStream) ArrayList(java.util.ArrayList) UnknownPortException(org.apache.nifi.remote.exception.UnknownPortException) CommunicationsSession(org.apache.nifi.remote.protocol.CommunicationsSession) SocketChannelCommunicationsSession(org.apache.nifi.remote.io.socket.SocketChannelCommunicationsSession) SSLSocketChannelCommunicationsSession(org.apache.nifi.remote.io.socket.ssl.SSLSocketChannelCommunicationsSession) URI(java.net.URI) FlowFileCodec(org.apache.nifi.remote.codec.FlowFileCodec) PeerStatus(org.apache.nifi.remote.PeerStatus) UnreachableClusterException(org.apache.nifi.remote.exception.UnreachableClusterException) PeerDescription(org.apache.nifi.remote.PeerDescription) Peer(org.apache.nifi.remote.Peer) IOException(java.io.IOException) DataInputStream(java.io.DataInputStream) SocketClientProtocol(org.apache.nifi.remote.protocol.socket.SocketClientProtocol) UnreachableClusterException(org.apache.nifi.remote.exception.UnreachableClusterException) HandshakeException(org.apache.nifi.remote.exception.HandshakeException) TransmissionDisabledException(org.apache.nifi.remote.exception.TransmissionDisabledException) PortNotRunningException(org.apache.nifi.remote.exception.PortNotRunningException) UnknownPortException(org.apache.nifi.remote.exception.UnknownPortException) IOException(java.io.IOException) CertificateException(java.security.cert.CertificateException) PortNotRunningException(org.apache.nifi.remote.exception.PortNotRunningException) HandshakeException(org.apache.nifi.remote.exception.HandshakeException)

Example 12 with PeerDescription

use of org.apache.nifi.remote.PeerDescription in project nifi by apache.

the class EndpointConnectionPool method fetchRemotePeerStatuses.

@Override
public Set<PeerStatus> fetchRemotePeerStatuses(final PeerDescription peerDescription) throws IOException {
    final String hostname = peerDescription.getHostname();
    final int port = peerDescription.getPort();
    final URI clusterUrl = siteInfoProvider.getActiveClusterUrl();
    final PeerDescription clusterPeerDescription = new PeerDescription(hostname, port, clusterUrl.toString().startsWith("https://"));
    final CommunicationsSession commsSession = establishSiteToSiteConnection(hostname, port);
    final Peer peer = new Peer(clusterPeerDescription, commsSession, "nifi://" + hostname + ":" + port, clusterUrl.toString());
    final SocketClientProtocol clientProtocol = new SocketClientProtocol();
    final DataInputStream dis = new DataInputStream(commsSession.getInput().getInputStream());
    final DataOutputStream dos = new DataOutputStream(commsSession.getOutput().getOutputStream());
    RemoteResourceInitiator.initiateResourceNegotiation(clientProtocol, dis, dos);
    clientProtocol.setTimeout(commsTimeout);
    if (clientProtocol.getVersionNegotiator().getVersion() < 5) {
        String portId = getPortIdentifier(TransferDirection.RECEIVE);
        if (portId == null) {
            portId = getPortIdentifier(TransferDirection.SEND);
        }
        if (portId == null) {
            peer.close();
            throw new IOException("Failed to determine the identifier of port " + remoteDestination.getName());
        }
        clientProtocol.handshake(peer, portId);
    } else {
        clientProtocol.handshake(peer, null);
    }
    final Set<PeerStatus> peerStatuses = clientProtocol.getPeerStatuses(peer);
    try {
        clientProtocol.shutdown(peer);
    } catch (final IOException e) {
        final String message = String.format("%s Failed to shutdown protocol when updating list of peers due to %s", this, e.toString());
        warn(logger, eventReporter, message);
        if (logger.isDebugEnabled()) {
            logger.warn("", e);
        }
    }
    try {
        peer.close();
    } catch (final IOException e) {
        final String message = String.format("%s Failed to close resources when updating list of peers due to %s", this, e.toString());
        warn(logger, eventReporter, message);
        if (logger.isDebugEnabled()) {
            logger.warn("", e);
        }
    }
    return peerStatuses;
}
Also used : PeerDescription(org.apache.nifi.remote.PeerDescription) DataOutputStream(java.io.DataOutputStream) PeerStatus(org.apache.nifi.remote.PeerStatus) Peer(org.apache.nifi.remote.Peer) CommunicationsSession(org.apache.nifi.remote.protocol.CommunicationsSession) SocketChannelCommunicationsSession(org.apache.nifi.remote.io.socket.SocketChannelCommunicationsSession) SSLSocketChannelCommunicationsSession(org.apache.nifi.remote.io.socket.ssl.SSLSocketChannelCommunicationsSession) IOException(java.io.IOException) DataInputStream(java.io.DataInputStream) URI(java.net.URI) SocketClientProtocol(org.apache.nifi.remote.protocol.socket.SocketClientProtocol)

Example 13 with PeerDescription

use of org.apache.nifi.remote.PeerDescription in project nifi by apache.

the class TestPeerSelector method testFetchRemotePeerStatuses.

/**
 * This test simulates a failure scenario of a remote NiFi cluster. It confirms that:
 * <ol>
 *     <li>PeerSelector uses the bootstrap node to fetch remote peer statuses at the initial attempt</li>
 *     <li>PeerSelector uses one of query-able nodes lastly fetched successfully</li>
 *     <li>PeerSelector can refresh remote peer statuses even if the bootstrap node is down</li>
 *     <li>PeerSelector returns null as next peer when there's no peer available</li>
 *     <li>PeerSelector always tries to fetch peer statuses at least from the bootstrap node, so that it can
 *     recover when the node gets back online</li>
 * </ol>
 */
@Test
public void testFetchRemotePeerStatuses() throws IOException {
    final Set<PeerStatus> peerStatuses = new HashSet<>();
    final PeerDescription bootstrapNode = new PeerDescription("Node1", 1111, true);
    final PeerDescription node2 = new PeerDescription("Node2", 2222, true);
    final PeerStatus bootstrapNodeStatus = new PeerStatus(bootstrapNode, 10, true);
    final PeerStatus node2Status = new PeerStatus(node2, 10, true);
    peerStatuses.add(bootstrapNodeStatus);
    peerStatuses.add(node2Status);
    final PeerStatusProvider peerStatusProvider = Mockito.mock(PeerStatusProvider.class);
    final PeerSelector peerSelector = new PeerSelector(peerStatusProvider, null);
    final UnitTestSystemTime systemTime = new UnitTestSystemTime();
    peerSelector.setSystemTime(systemTime);
    doReturn(bootstrapNode).when(peerStatusProvider).getBootstrapPeerDescription();
    doAnswer(invocation -> {
        final PeerDescription peerFetchStatusesFrom = invocation.getArgumentAt(0, PeerDescription.class);
        if (peerStatuses.stream().filter(ps -> ps.getPeerDescription().equals(peerFetchStatusesFrom)).collect(Collectors.toSet()).size() > 0) {
            // If the remote peer is running, then return available peer statuses.
            return peerStatuses;
        }
        throw new IOException("Connection refused. " + peerFetchStatusesFrom + " is not running.");
    }).when(peerStatusProvider).fetchRemotePeerStatuses(any(PeerDescription.class));
    // 1st attempt. It uses the bootstrap node.
    peerSelector.refreshPeers();
    PeerStatus peerStatus = peerSelector.getNextPeerStatus(TransferDirection.RECEIVE);
    assertNotNull(peerStatus);
    // Proceed time so that peer selector refresh statuses.
    peerStatuses.remove(bootstrapNodeStatus);
    systemTime.offset += TimeUnit.MILLISECONDS.convert(1, TimeUnit.MINUTES) + 1;
    // 2nd attempt.
    peerSelector.refreshPeers();
    peerStatus = peerSelector.getNextPeerStatus(TransferDirection.RECEIVE);
    assertNotNull(peerStatus);
    assertEquals("Node2 should be returned since node 2 is the only available node.", node2, peerStatus.getPeerDescription());
    // Proceed time so that peer selector refresh statuses.
    systemTime.offset += TimeUnit.MILLISECONDS.convert(1, TimeUnit.MINUTES) + 1;
    // 3rd attempt.
    peerSelector.refreshPeers();
    peerStatus = peerSelector.getNextPeerStatus(TransferDirection.RECEIVE);
    assertNotNull(peerStatus);
    assertEquals("Node2 should be returned since node 2 is the only available node.", node2, peerStatus.getPeerDescription());
    // Remove node2 to simulate that it goes down. There's no available node at this point.
    peerStatuses.remove(node2Status);
    systemTime.offset += TimeUnit.MILLISECONDS.convert(1, TimeUnit.MINUTES) + 1;
    peerSelector.refreshPeers();
    peerStatus = peerSelector.getNextPeerStatus(TransferDirection.RECEIVE);
    assertNull("PeerSelector should return null as next peer status, since there's no available peer", peerStatus);
    // Add node1 back. PeerSelector should be able to fetch peer statuses because it always tries to fetch at least from the bootstrap node.
    peerStatuses.add(bootstrapNodeStatus);
    systemTime.offset += TimeUnit.MILLISECONDS.convert(1, TimeUnit.MINUTES) + 1;
    peerSelector.refreshPeers();
    peerStatus = peerSelector.getNextPeerStatus(TransferDirection.RECEIVE);
    assertEquals("Node1 should be returned since node 1 is the only available node.", bootstrapNode, peerStatus.getPeerDescription());
}
Also used : LoggerFactory(org.slf4j.LoggerFactory) Collectors.groupingBy(java.util.stream.Collectors.groupingBy) HashSet(java.util.HashSet) Collectors.toMap(java.util.stream.Collectors.toMap) PeerStatus(org.apache.nifi.remote.PeerStatus) Map(java.util.Map) Mockito.doAnswer(org.mockito.Mockito.doAnswer) Assert.fail(org.junit.Assert.fail) Mockito.doReturn(org.mockito.Mockito.doReturn) TransferDirection(org.apache.nifi.remote.TransferDirection) Logger(org.slf4j.Logger) PeerDescription(org.apache.nifi.remote.PeerDescription) Assert.assertNotNull(org.junit.Assert.assertNotNull) Set(java.util.Set) Assert.assertTrue(org.junit.Assert.assertTrue) Test(org.junit.Test) IOException(java.io.IOException) Collectors.reducing(java.util.stream.Collectors.reducing) Collectors(java.util.stream.Collectors) TimeUnit(java.util.concurrent.TimeUnit) Matchers.any(org.mockito.Matchers.any) Mockito(org.mockito.Mockito) List(java.util.List) Assert.assertNull(org.junit.Assert.assertNull) Assert.assertEquals(org.junit.Assert.assertEquals) PeerDescription(org.apache.nifi.remote.PeerDescription) PeerStatus(org.apache.nifi.remote.PeerStatus) IOException(java.io.IOException) HashSet(java.util.HashSet) Test(org.junit.Test)

Example 14 with PeerDescription

use of org.apache.nifi.remote.PeerDescription in project nifi by apache.

the class TestHttpClientTransaction method getClientTransaction.

private HttpClientTransaction getClientTransaction(InputStream is, OutputStream os, SiteToSiteRestApiClient apiClient, TransferDirection direction, String transactionUrl) throws IOException {
    PeerDescription description = null;
    String peerUrl = "";
    HttpCommunicationsSession commsSession = new HttpCommunicationsSession();
    ((HttpInput) commsSession.getInput()).setInputStream(is);
    ((HttpOutput) commsSession.getOutput()).setOutputStream(os);
    String clusterUrl = "";
    Peer peer = new Peer(description, commsSession, peerUrl, clusterUrl);
    String portId = "portId";
    boolean useCompression = false;
    int penaltyMillis = 1000;
    EventReporter eventReporter = new EventReporter() {

        @Override
        public void reportEvent(Severity severity, String category, String message) {
            logger.info("Reporting event... severity={}, category={}, message={}", severity, category, message);
        }
    };
    int protocolVersion = 5;
    HttpClientTransaction transaction = new HttpClientTransaction(protocolVersion, peer, direction, useCompression, portId, penaltyMillis, eventReporter);
    transaction.initialize(apiClient, transactionUrl);
    return transaction;
}
Also used : HttpInput(org.apache.nifi.remote.io.http.HttpInput) PeerDescription(org.apache.nifi.remote.PeerDescription) HttpCommunicationsSession(org.apache.nifi.remote.io.http.HttpCommunicationsSession) Peer(org.apache.nifi.remote.Peer) Severity(org.apache.nifi.reporting.Severity) HttpOutput(org.apache.nifi.remote.io.http.HttpOutput) EventReporter(org.apache.nifi.events.EventReporter)

Example 15 with PeerDescription

use of org.apache.nifi.remote.PeerDescription in project nifi by apache.

the class TestSocketClientTransaction method getClientTransaction.

private SocketClientTransaction getClientTransaction(ByteArrayInputStream bis, ByteArrayOutputStream bos, TransferDirection direction) throws IOException {
    PeerDescription description = null;
    String peerUrl = "";
    SocketChannelCommunicationsSession commsSession = mock(SocketChannelCommunicationsSession.class);
    SocketChannelInput socketIn = mock(SocketChannelInput.class);
    SocketChannelOutput socketOut = mock(SocketChannelOutput.class);
    when(commsSession.getInput()).thenReturn(socketIn);
    when(commsSession.getOutput()).thenReturn(socketOut);
    when(socketIn.getInputStream()).thenReturn(bis);
    when(socketOut.getOutputStream()).thenReturn(bos);
    String clusterUrl = "";
    Peer peer = new Peer(description, commsSession, peerUrl, clusterUrl);
    boolean useCompression = false;
    int penaltyMillis = 1000;
    EventReporter eventReporter = null;
    int protocolVersion = 5;
    String destinationId = "destinationId";
    return new SocketClientTransaction(protocolVersion, destinationId, peer, codec, direction, useCompression, penaltyMillis, eventReporter);
}
Also used : PeerDescription(org.apache.nifi.remote.PeerDescription) SocketChannelInput(org.apache.nifi.remote.io.socket.SocketChannelInput) Peer(org.apache.nifi.remote.Peer) SocketChannelOutput(org.apache.nifi.remote.io.socket.SocketChannelOutput) SocketChannelCommunicationsSession(org.apache.nifi.remote.io.socket.SocketChannelCommunicationsSession) EventReporter(org.apache.nifi.events.EventReporter)

Aggregations

PeerDescription (org.apache.nifi.remote.PeerDescription)18 PeerStatus (org.apache.nifi.remote.PeerStatus)13 HashSet (java.util.HashSet)10 Peer (org.apache.nifi.remote.Peer)9 IOException (java.io.IOException)6 Test (org.junit.Test)5 DataInputStream (java.io.DataInputStream)4 DataOutputStream (java.io.DataOutputStream)4 InputStream (java.io.InputStream)4 SocketChannelCommunicationsSession (org.apache.nifi.remote.io.socket.SocketChannelCommunicationsSession)4 CommunicationsSession (org.apache.nifi.remote.protocol.CommunicationsSession)4 OutputStream (java.io.OutputStream)3 URI (java.net.URI)3 Set (java.util.Set)3 TimeUnit (java.util.concurrent.TimeUnit)3 Collectors (java.util.stream.Collectors)3 EventReporter (org.apache.nifi.events.EventReporter)3 TransferDirection (org.apache.nifi.remote.TransferDirection)3 Logger (org.slf4j.Logger)3 LoggerFactory (org.slf4j.LoggerFactory)3