package org.jgroups.protocols;
import org.jgroups.*;
import org.jgroups.annotations.*;
import org.jgroups.conf.PropertyConverters;
import org.jgroups.stack.IpAddress;
import org.jgroups.stack.Protocol;
import org.jgroups.util.*;
import org.jgroups.util.ThreadFactory;
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;
/**
* 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, and 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 FD_SOCK protocol will work for groups where members are on different hosts
* 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=null;
@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=null;
@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=0;
@Property(name="bind_interface", converter=PropertyConverters.BindInterface.class,
description="The interface (NIC) which should be used by this transport", dependsUpon="bind_addr")
protected String bind_interface_str=null;
@Property(description="Timeout for getting socket cache from coordinator. Default is 1000 msec")
protected long get_cache_timeout=1000;
@Property(description="Interval for broadcasting suspect messages. Default is 5000 msec")
protected long suspect_msg_interval=5000;
@Property(description="Number of attempts coordinator is solicited for socket cache until we give up. Default is 3")
protected int num_tries=3;
@Property(description="Start port for server socket. Default value of 0 picks a random port")
protected int start_port=0;
@Property(description="Start port for client socket. Default value of 0 picks a random port")
protected int client_bind_port=0;
@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")
protected int sock_conn_timeout=1000;
/* --------------------------------------------- JMX ------------------------------------------------------ */
protected int num_suspect_events=0;
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 CopyOnWriteArraySet<>();
protected final List pingable_mbrs=new CopyOnWriteArrayList<>();
protected volatile boolean srv_sock_sent=false; // has own socket been broadcast yet ?
/** Used to rendezvous on GET_CACHE and GET_CACHE_RSP */
protected final Promise