Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/* This file is part of VoltDB.
* Copyright (C) 2008-2020 VoltDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with VoltDB. If not, see .
*/
package org.voltcore.messaging;
import java.io.Closeable;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedByInterruptException;
import java.nio.channels.ClosedSelectorException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.net.ssl.SSLEngine;
import org.json_voltpatches.JSONArray;
import org.json_voltpatches.JSONException;
import org.json_voltpatches.JSONObject;
import org.voltcore.logging.Level;
import org.voltcore.logging.VoltLogger;
import org.voltcore.network.ReverseDNSCache;
import org.voltcore.utils.CoreUtils;
import org.voltcore.utils.VersionChecker;
import org.voltcore.utils.ssl.MessagingChannel;
import org.voltcore.utils.ssl.SSLConfiguration;
import org.voltdb.client.TLSHandshaker;
import org.voltdb.utils.MiscUtils;
import com.google_voltpatches.common.collect.ImmutableMap;
import com.google_voltpatches.common.collect.ImmutableSet;
import com.google_voltpatches.common.collect.Sets;
import com.google_voltpatches.common.net.HostAndPort;
import io.netty.buffer.ByteBufAllocator;
import io.netty.handler.ssl.SslContext;
/**
* SocketJoiner runs all the time listening for new nodes in the cluster. Since it is a dedicated thread
* it is able to block while a new node joins without disrupting other activities.
*
* At startup socket joiner will connect to the rest of the cluster in the start method if it fails
* to bind to the leader address.
*
* If it binds to the leader address and becomes the leader the start method returns immediately and runPrimary
* is run from a separate thread. runPrimary will wait for the countdown latch for bootstrapping zk to count down
* before accepting new connections
*/
public class SocketJoiner {
static final String HOSTS = "hosts";
static final String REPORTED_ADDRESS = "reportedAddress";
static final String NEW_HOST_ID = "newHostId";
static final String REASON = "reason";
static final String MAY_RETRY = "mayRetry";
static final String ACCEPTED = "accepted";
private static final String MAY_EXCHANGE_TS = "mayExchangeTs";
private static final String TYPE = "type";
static final String HOST_ID = "hostId";
static final String PORT = "port";
static final String ADDRESS = "address";
private static final String VERSION_COMPATIBLE = "versionCompatible";
private static final String BUILD_STRING = "buildString";
public static final String VERSION_STRING = "versionString";
private static final int MAX_CLOCKSKEW = Integer.getInteger("MAX_CLOCKSKEW", 200);
private static final int RETRY_INTERVAL = Integer.getInteger("MESH_JOIN_RETRY_INTERVAL", 10);
private static final int RETRY_INTERVAL_SALT = Integer.getInteger("MESH_JOIN_RETRY_INTERVAL_SALT", 30);
private static final int CRITICAL_CLOCKSKEW = 100;
private static final int NAME_LOOKUP_RETRY_MS = 1000;
private static final int NAME_LOOKUP_RETRY_LIMIT = 10;
private static final int SOCKET_CONNECT_RETRY_MS = 250;
public static final String FAIL_ESTABLISH_MESH_MSG = "Failed to establish socket mesh.";
enum ConnectionType {
REQUEST_HOSTID,
PUBLISH_HOSTID,
REQUEST_CONNECTION;
}
/**
* Exception for wrapping retryable connect failures
*/
private static final class SocketRetryException extends Exception {
private static final long serialVersionUID = 1L;
public SocketRetryException(Throwable cause) {
super(cause);
}
}
/**
* Interface into host messenger to notify it of new connections.
*
*/
public interface JoinHandler {
/*
* Notify that a specific host has joined with the specified host id.
*/
public void notifyOfJoin(
int hostId,
SocketChannel socket,
SSLEngine sslEngine,
InetSocketAddress listeningAddress,
JSONObject jo);
/*
* A node wants to join the socket mesh
*/
public void requestJoin(
SocketChannel socket,
SSLEngine sslEngine,
MessagingChannel messagingChannel,
InetSocketAddress listeningAddress,
JSONObject jo) throws Exception;
/*
* A connection has been made to all of the specified hosts. Invoked by
* nodes connected to the cluster
*/
public void notifyOfHosts(
int yourLocalHostId,
int hosts[],
SocketChannel sockets[],
SSLEngine[] sslEngines,
InetSocketAddress listeningAddresses[],
Map jos) throws Exception;
/*
* Create new connection between given node and current node
*/
public void notifyOfConnection(
int hostId,
SocketChannel socket,
SSLEngine sslEngine,
InetSocketAddress listeningAddress) throws Exception;
}
private static class RequestHostIdResponse {
final private JSONObject m_leaderInfo;
final private JSONObject m_responseBody;
public RequestHostIdResponse(JSONObject leaderInfo, JSONObject responseBody) {
m_leaderInfo = leaderInfo;
m_responseBody = responseBody;
}
JSONObject getLeaderInfo() { return m_leaderInfo; }
JSONObject getResponseBody() { return m_responseBody; }
}
private static final VoltLogger LOG = new VoltLogger("JOINER");
private static final VoltLogger consoleLog = new VoltLogger("CONSOLE");
private static final VoltLogger hostLog = new VoltLogger("HOST");
private final ExecutorService m_es = CoreUtils.getSingleThreadExecutor("Socket Joiner");
InetSocketAddress m_coordIp = null;
int m_localHostId = 0;
private final List m_listenerSockets = new ArrayList();
private Selector m_selector;
private final JoinHandler m_joinHandler;
// from configuration data
int m_internalPort = 3021;
String m_internalInterface = "";
/*
* The interface we connected to the leader on
*/
String m_reportedInternalInterface;
public boolean start(final CountDownLatch externalInitBarrier) {
boolean retval = false;
/*
* Probe coordinator host list for leader candidates that are operational
* (i.e. node state is operational)
*/
m_coordIp = null;
for (String coordHost: m_acceptor.getCoordinators()) {
if (m_coordIp != null) {
break;
}
/*
* On an operational leader (i.e. node is up) the request to join the cluster
* may be rejected, e.g. multiple hosts rejoining at the same time. In this case,
* the code will retry.
*/
long retryInterval = RETRY_INTERVAL;
int nameRetries = NAME_LOOKUP_RETRY_LIMIT;
final Random salt = new Random();
while (true) {
InetSocketAddress primary = null;
try {
primary = addressFromHost(coordHost);
connectToPrimary(primary);
// m_coordIp is now filled in
break;
} catch (UnknownHostException e) {
if (--nameRetries <= 0) {
warnInfrequently("Unknown host name '%s', no more retries", coordHost);
break; // no more retries; move on to next potential coordinator
}
warnInfrequently("Unknown host name '%s', retrying", coordHost);
safeSleep(NAME_LOOKUP_RETRY_MS);
} catch (SocketRetryException e) {
LOG.debug(String.format("Cannot connect to %s. %s", primary, e.getMessage()));
m_coordIp = null; // no retry; move on to next potential coordinator
break;
} catch (CoreUtils.RetryException e) {
LOG.warn(String.format("Request to join cluster mesh is rejected, retrying in %d seconds. %s",
retryInterval, e.getMessage()));
safeSleep(TimeUnit.SECONDS.toMillis(retryInterval));
// exponential back off with a salt to avoid collision. Max is 5 minutes.
retryInterval = (Math.min(retryInterval * 2, TimeUnit.MINUTES.toSeconds(5)) +
salt.nextInt(RETRY_INTERVAL_SALT));
//Over waiting may occur in some cases.
//For example, there are 4 rejoining nodes. Node 1 may take over 5 min to be completed.
//Nodes 2 to 4 continue to wait after they detect that node 1 is still rejoining right before its rejoining is completed
//They will wait 5 min + salt before sending another rejoining request. All the following rejoining requests are sent
//after 5 min + salt. Reset waiting time to avoid over waiting.
if (retryInterval > TimeUnit.MINUTES.toSeconds(5)) {
retryInterval = RETRY_INTERVAL;
}
} catch (Exception e) {
String s = m_coordIp != null ? m_coordIp.toString() : coordHost;
hostLog.error("Failed to establish socket mesh.", e);
throw new RuntimeException("Failed to establish socket mesh with " + s, e);
}
}
}
/*
* m_coordIp will have been set by connectToPrimary if we
* succeeded in finding a leader. Otherwise we need to
* appoint one.
*/
if (m_coordIp == null) {
String leader = m_acceptor.getLeader().toString(); // may have host name
InetSocketAddress leaderAddr = null; // resolved to address and port
/*
* Attempt to become the leader by binding to the specified address
* (but only if the port is the correct one for our configuration).
* Note: no retry for unresolved hostname here; we assume if a
* name is not known, it cannot be our name, and therefore there
* is no point in waiting so we can try to bind to that address.
*/
try {
leaderAddr = addressFromHost(leader); // may throw for unresolved host name
if (leaderAddr.getPort() == m_internalPort) {
hostLog.info("Attempting to bind to leader ip " + leaderAddr.getAddress().getHostAddress());
ServerSocketChannel listenerSocket = ServerSocketChannel.open();
listenerSocket.socket().bind(leaderAddr);
listenerSocket.socket().setPerformancePreferences(0, 2, 1);
listenerSocket.configureBlocking(false);
m_listenerSockets.add(listenerSocket);
}
}
catch (IOException e) {
if (!m_listenerSockets.isEmpty()) {
try {
m_listenerSockets.get(0).close();
m_listenerSockets.clear();
}
catch (IOException ex) {
new VoltLogger(SocketJoiner.class.getName()).l7dlog(Level.FATAL, null, ex);
}
}
}
// There's a listener socket if and only if we're the leader
if (!m_listenerSockets.isEmpty()) {
// If an internal interface was specified, see if it matches any
// of the forms of the leader address we've bound to.
if (m_internalInterface != null && !m_internalInterface.equals("")) {
InetAddress testAddr = leaderAddr.getAddress();
if (!m_internalInterface.equals(ReverseDNSCache.hostnameOrAddress(testAddr)) &&
!m_internalInterface.equals(testAddr.getCanonicalHostName()) &&
!m_internalInterface.equals(testAddr.getHostAddress())) {
String msg = String.format("The provided internal interface (%s) does not match the "
+ "specified leader address (%s, %s). "
+ "This will result in either a cluster which fails to start or an unintended network topology. "
+ "The leader will now exit; correct your specified leader and interface and try restarting.",
m_internalInterface,
ReverseDNSCache.hostnameOrAddress(testAddr),
testAddr.getHostAddress());
org.voltdb.VoltDB.crashLocalVoltDB(msg, false, null);
}
}
// We are the leader
consoleLog.info("Connecting to VoltDB cluster as the leader...");
m_coordIp = leaderAddr;
retval = true;
/*
* Need to wait for external initialization to complete before
* accepting new connections. This is slang for the leader
* creating an agreement site that agrees with itself
*/
m_es.submit(new Callable