package org.jgroups.protocols;
import org.jgroups.*;
import org.jgroups.annotations.*;
import org.jgroups.blocks.LazyRemovalCache;
import org.jgroups.conf.AttributeType;
import org.jgroups.stack.IpAddress;
import org.jgroups.stack.Protocol;
import org.jgroups.util.*;
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Supplier;
/**
* Failure detection protocol based on sockets. Failure detection is ring-based. Each member creates a
* server socket and announces its address together with the server socket's address in a multicast.
*
* A pinger thread will be started when the membership goes above 1 and will be stopped when it drops below
* 2. The pinger thread connects to its neighbor on the right and waits until the socket is closed. When
* the socket is closed by the monitored peer in an abnormal fashion (IOException), the neighbor will be
* suspected.
*
* The main feature of this protocol is that no ping messages need to be exchanged between any 2 peers, as failure
* detection relies entirely on TCP sockets. The advantage is that no activity will take place between 2 peers as long
* as they are alive (i.e. have their server sockets open). The disadvantage is that hung servers or crashed routers
* will not cause sockets to be closed, therefore they won't be detected.
*
* The costs involved are 2 additional threads: one that monitors the client side of the socket connection
* (to monitor a peer) and another one that manages the server socket. However, those threads will be idle as long as
* both peers are running.
* @author Bela Ban May 29 2001
*/
@MBean(description="Failure detection protocol based on sockets connecting members")
public class FD_SOCK extends Protocol implements Runnable {
protected static final int NORMAL_TERMINATION=9;
protected static final int ABNORMAL_TERMINATION=-1;
/* ----------------------------------------- Properties -------------------------------------------------- */
@LocalAddress
@Property(description="The NIC on which the ServerSocket should listen on. " +
"The following special values are also recognized: GLOBAL, SITE_LOCAL, LINK_LOCAL and NON_LOOPBACK",
systemProperty={Global.BIND_ADDR},writable=false)
protected InetAddress bind_addr;
@Property(description="Use \"external_addr\" if you have hosts on different networks, behind " +
"firewalls. On each firewall, set up a port forwarding rule (sometimes called \"virtual server\") to " +
"the local IP (e.g. 192.168.1.100) of the host then on each host, set \"external_addr\" TCP transport " +
"parameter to the external (public IP) address of the firewall.",
systemProperty=Global.EXTERNAL_ADDR,writable=false)
protected InetAddress external_addr;
@Property(description="Used to map the internal port (bind_port) to an external port. Only used if > 0",
systemProperty=Global.EXTERNAL_PORT,writable=false)
protected int external_port;
@Property(description="Timeout for getting socket cache from coordinator",type=AttributeType.TIME)
protected long get_cache_timeout=1000;
@Property(description="Max number of elements in the cache until deleted elements are removed")
protected int cache_max_elements=200;
@Property(description="Max age (in ms) an element marked as removed has to have until it is removed",
type=AttributeType.TIME)
protected long cache_max_age=10000;
@Property(description="Interval for broadcasting suspect messages",type=AttributeType.TIME)
protected long suspect_msg_interval=5000;
@Property(description="Number of attempts coordinator is solicited for socket cache until we give up")
protected int num_tries=3;
@Property(description="Start port for server socket. Default value of 0 picks a random port")
protected int start_port;
@Property(description="Start port for client socket. Default value of 0 picks a random port")
protected int client_bind_port;
@Property(description="Number of ports to probe for start_port and client_bind_port")
protected int port_range=50;
@Property(description="Whether to use KEEP_ALIVE on the ping socket or not. Default is true")
protected boolean keep_alive=true;
@Property(description="Max time in millis to wait for ping Socket.connect() to return",type=AttributeType.TIME)
protected int sock_conn_timeout=1000;
/* --------------------------------------------- JMX ------------------------------------------------------ */
protected int num_suspect_events;
protected final BoundedList suspect_history=new BoundedList<>(20);
/* --------------------------------------------- Fields ------------------------------------------------------ */
protected volatile List members=new ArrayList<>(11); // volatile eliminates the lock
protected final Set suspected_mbrs=new ConcurrentSkipListSet<>();
protected final List pingable_mbrs=new ArrayList<>();
protected volatile boolean srv_sock_sent; // has own socket been broadcast yet ?
/** Used to rendezvous on GET_CACHE and GET_CACHE_RSP */
protected final Promise