All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.elasticsearch.discovery.zen.ping.unicast.UnicastZenPing Maven / Gradle / Ivy

There is a newer version: 8.13.4
Show newest version
/*
 * Licensed to Elastic Search and Shay Banon under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. Elastic Search licenses this
 * file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.elasticsearch.discovery.zen.ping.unicast;

import org.elasticsearch.ElasticSearchException;
import org.elasticsearch.ElasticSearchIllegalArgumentException;
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.cluster.node.DiscoveryNodes;
import org.elasticsearch.discovery.zen.DiscoveryNodesProvider;
import org.elasticsearch.discovery.zen.ping.ZenPing;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.*;
import org.elasticsearch.util.Strings;
import org.elasticsearch.util.TimeValue;
import org.elasticsearch.util.collect.ImmutableList;
import org.elasticsearch.util.collect.Lists;
import org.elasticsearch.util.component.AbstractLifecycleComponent;
import org.elasticsearch.util.concurrent.jsr166y.LinkedTransferQueue;
import org.elasticsearch.util.io.stream.StreamInput;
import org.elasticsearch.util.io.stream.StreamOutput;
import org.elasticsearch.util.io.stream.Streamable;
import org.elasticsearch.util.settings.Settings;
import org.elasticsearch.util.transport.TransportAddress;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

import static org.elasticsearch.discovery.zen.ping.ZenPing.PingResponse.*;
import static org.elasticsearch.util.TimeValue.*;
import static org.elasticsearch.util.collect.Lists.*;
import static org.elasticsearch.util.concurrent.ConcurrentCollections.*;
import static org.elasticsearch.util.settings.ImmutableSettings.Builder.*;

/**
 * @author kimchy (shay.banon)
 */
public class UnicastZenPing extends AbstractLifecycleComponent implements ZenPing {

    private final ThreadPool threadPool;

    private final TransportService transportService;

    private final ClusterName clusterName;


    private final DiscoveryNode[] nodes;

    private volatile DiscoveryNodesProvider nodesProvider;

    private final AtomicInteger pingIdGenerator = new AtomicInteger();

    private final Map> receivedResponses = newConcurrentMap();

    // a list of temporal responses a node will return for a request (holds requests from other nodes)
    private final Queue temporalResponses = new LinkedTransferQueue();

    public UnicastZenPing(ThreadPool threadPool, TransportService transportService, ClusterName clusterName) {
        this(EMPTY_SETTINGS, threadPool, transportService, clusterName);
    }

    public UnicastZenPing(Settings settings, ThreadPool threadPool, TransportService transportService, ClusterName clusterName) {
        super(settings);
        this.threadPool = threadPool;
        this.transportService = transportService;
        this.clusterName = clusterName;

        List hosts = Lists.newArrayList(componentSettings.getAsArray("hosts"));
        if (componentSettings.get("hosts") != null) {
            hosts.addAll(Strings.commaDelimitedListToSet(componentSettings.get("hosts")));
        }

        logger.debug("Using hosts ", hosts);

        List nodes = Lists.newArrayList();
        int idCounter = 0;
        for (String host : hosts) {
            try {
                for (TransportAddress address : transportService.addressesFromString(host)) {
                    nodes.add(new DiscoveryNode("#zen_unicast_" + (++idCounter) + "#", address));
                }
            } catch (Exception e) {
                throw new ElasticSearchIllegalArgumentException("Failed to resolve address for [" + host + "]", e);
            }
        }
        this.nodes = nodes.toArray(new DiscoveryNode[nodes.size()]);

        transportService.registerHandler(UnicastPingRequestHandler.ACTION, new UnicastPingRequestHandler());
    }

    @Override protected void doStart() throws ElasticSearchException {
    }

    @Override protected void doStop() throws ElasticSearchException {
    }

    @Override protected void doClose() throws ElasticSearchException {
        transportService.removeHandler(UnicastPingRequestHandler.ACTION);
    }

    protected List buildDynamicNodes() {
        return ImmutableList.of();
    }

    @Override public void setNodesProvider(DiscoveryNodesProvider nodesProvider) {
        this.nodesProvider = nodesProvider;
    }

    public PingResponse[] pingAndWait(TimeValue timeout) {
        final AtomicReference response = new AtomicReference();
        final CountDownLatch latch = new CountDownLatch(1);
        ping(new PingListener() {
            @Override public void onPing(PingResponse[] pings) {
                response.set(pings);
                latch.countDown();
            }
        }, timeout);
        try {
            latch.await();
            return response.get();
        } catch (InterruptedException e) {
            return null;
        }
    }

    @Override public void ping(final PingListener listener, final TimeValue timeout) throws ElasticSearchException {
        final int id = pingIdGenerator.incrementAndGet();
        receivedResponses.put(id, new ConcurrentHashMap());
        sendPings(id, timeout, false);
        threadPool.schedule(new Runnable() {
            @Override public void run() {
                sendPings(id, timeout, true);
                ConcurrentMap responses = receivedResponses.remove(id);
                listener.onPing(responses.values().toArray(new PingResponse[responses.size()]));
            }
        }, timeout);
    }

    private void sendPings(int id, TimeValue timeout, boolean wait) {
        UnicastPingRequest pingRequest = new UnicastPingRequest();
        pingRequest.id = id;
        pingRequest.timeout = timeout;
        DiscoveryNodes discoNodes = nodesProvider.nodes();
        pingRequest.pingResponse = new PingResponse(discoNodes.localNode(), discoNodes.masterNode(), clusterName);

        List nodesToPing = newArrayList(nodes);
        nodesToPing.addAll(buildDynamicNodes());

        final CountDownLatch latch = new CountDownLatch(nodesToPing.size());
        for (final DiscoveryNode node : nodesToPing) {
            // make sure we are connected
            boolean disconnectX;
            DiscoveryNode nodeToSendX = discoNodes.findByAddress(node.address());
            if (nodeToSendX != null) {
                disconnectX = false;
            } else {
                nodeToSendX = node;
                disconnectX = true;
            }
            final DiscoveryNode nodeToSend = nodeToSendX;
            try {
                transportService.connectToNode(nodeToSend);
            } catch (ConnectTransportException e) {
                latch.countDown();
                // can't connect to the node
                continue;
            }

            final boolean disconnect = disconnectX;
            transportService.sendRequest(nodeToSend, UnicastPingRequestHandler.ACTION, pingRequest, TimeValue.timeValueMillis((long) (timeout.millis() * 1.25)), new BaseTransportResponseHandler() {

                @Override public UnicastPingResponse newInstance() {
                    return new UnicastPingResponse();
                }

                @Override public void handleResponse(UnicastPingResponse response) {
                    try {
                        DiscoveryNodes discoveryNodes = nodesProvider.nodes();
                        for (PingResponse pingResponse : response.pingResponses) {
                            if (disconnect) {
                                transportService.disconnectFromNode(nodeToSend);
                            }
                            if (pingResponse.target().id().equals(discoveryNodes.localNodeId())) {
                                // that's us, ignore
                                continue;
                            }
                            if (!pingResponse.clusterName().equals(clusterName)) {
                                // not part of the cluster
                                return;
                            }
                            ConcurrentMap responses = receivedResponses.get(response.id);
                            if (responses == null) {
                                logger.warn("Received ping response with no matching id [{}]", response.id);
                            } else {
                                responses.put(pingResponse.target(), pingResponse);
                            }
                        }
                    } finally {
                        latch.countDown();
                    }
                }

                @Override public void handleException(RemoteTransportException exp) {
                    latch.countDown();
                    if (exp instanceof ConnectTransportException) {
                        // ok, not connected...
                    } else {
                        if (disconnect) {
                            transportService.disconnectFromNode(nodeToSend);
                        }
                        logger.warn("Failed to send ping to [{}]", exp, node);
                    }
                }
            });
        }
        if (wait) {
            try {
                latch.await(timeout.millis() * 5, TimeUnit.MILLISECONDS);
            } catch (InterruptedException e) {
                // ignore
            }
        }
    }

    private UnicastPingResponse handlePingRequest(final UnicastPingRequest request) {
        temporalResponses.add(request.pingResponse);
        threadPool.schedule(new Runnable() {
            @Override public void run() {
                temporalResponses.remove(request.pingResponse);
            }
        }, request.timeout.millis() * 2, TimeUnit.MILLISECONDS);

        List pingResponses = newArrayList(temporalResponses);
        DiscoveryNodes discoNodes = nodesProvider.nodes();
        pingResponses.add(new PingResponse(discoNodes.localNode(), discoNodes.masterNode(), clusterName));


        UnicastPingResponse unicastPingResponse = new UnicastPingResponse();
        unicastPingResponse.id = request.id;
        unicastPingResponse.pingResponses = pingResponses.toArray(new PingResponse[pingResponses.size()]);

        return unicastPingResponse;
    }

    class UnicastPingRequestHandler extends BaseTransportRequestHandler {

        static final String ACTION = "discovery/zen/unicast";

        @Override public UnicastPingRequest newInstance() {
            return new UnicastPingRequest();
        }

        @Override public void messageReceived(UnicastPingRequest request, TransportChannel channel) throws Exception {
            channel.sendResponse(handlePingRequest(request));
        }
    }

    static class UnicastPingRequest implements Streamable {

        int id;

        TimeValue timeout;

        PingResponse pingResponse;

        UnicastPingRequest() {
        }

        @Override public void readFrom(StreamInput in) throws IOException {
            id = in.readInt();
            timeout = readTimeValue(in);
            pingResponse = readPingResponse(in);
        }

        @Override public void writeTo(StreamOutput out) throws IOException {
            out.writeInt(id);
            timeout.writeTo(out);
            pingResponse.writeTo(out);
        }
    }

    static class UnicastPingResponse implements Streamable {

        int id;

        PingResponse[] pingResponses;

        UnicastPingResponse() {
        }

        @Override public void readFrom(StreamInput in) throws IOException {
            id = in.readInt();
            pingResponses = new PingResponse[in.readVInt()];
            for (int i = 0; i < pingResponses.length; i++) {
                pingResponses[i] = readPingResponse(in);
            }
        }

        @Override public void writeTo(StreamOutput out) throws IOException {
            out.writeInt(id);
            out.writeVInt(pingResponses.length);
            for (PingResponse pingResponse : pingResponses) {
                pingResponse.writeTo(out);
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy