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

org.jgroups.stack.RouterStubManager Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 34.0.0.Final
Show newest version

package org.jgroups.stack;

import org.jgroups.Address;
import org.jgroups.PhysicalAddress;
import org.jgroups.logging.Log;
import org.jgroups.logging.LogFactory;
import org.jgroups.util.SocketFactory;
import org.jgroups.util.TimeScheduler;
import org.jgroups.util.Util;

import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * Manages a list of RouterStubs (e.g. health checking, reconnecting etc.
 * @author Vladimir Blagojevic
 * @author Bela Ban
 */
public class RouterStubManager implements Runnable, RouterStub.CloseListener {
    protected final List stubs=new ArrayList<>();
    protected final TimeScheduler    timer;
    protected final String           cluster_name;
    protected final Address          local_addr;
    protected final String           logical_name;
    protected final PhysicalAddress  phys_addr;
    protected final long             reconnect_interval; // reconnect interval (ms)
    protected boolean                use_nio=true;       // whether to use TcpClient or NioClient
    protected Future              reconnector_task, heartbeat_task, timeout_checker_task;
    protected final Log              log;
    protected SocketFactory          socket_factory;
    // Sends a heartbeat to the GossipRouter every heartbeat_interval ms (0 disables this)
    protected long                   heartbeat_interval;
    // Max time (ms) with no received message or heartbeat after which the connection to a GossipRouter is closed.
    // Ignored when heartbeat_interval is 0.
    protected long                   heartbeat_timeout;
    protected final Runnable         send_heartbeat=this::sendHeartbeat;
    protected final Runnable         check_timeouts=this::checkTimeouts;


    public RouterStubManager(Log log, TimeScheduler timer, String cluster_name, Address local_addr,
                             String logical_name, PhysicalAddress phys_addr, long reconnect_interval) {
        this.log=log != null? log : LogFactory.getLog(RouterStubManager.class);
        this.timer=timer;
        this.cluster_name=cluster_name;
        this.local_addr=local_addr;
        this.logical_name=logical_name;
        this.phys_addr=phys_addr;
        this.reconnect_interval=reconnect_interval;
    }

    public static RouterStubManager emptyGossipClientStubManager(Log log, TimeScheduler timer) {
        return new RouterStubManager(log, timer,null,null,null, null,0L);
    }
    

    public RouterStubManager useNio(boolean flag) {use_nio=flag; return this;}
    public boolean           reconnectorRunning() {return reconnector_task != null && !reconnector_task.isDone();}
    public boolean           heartbeaterRunning() {return heartbeat_task != null && !heartbeat_task.isDone();}
    public boolean           timeouterRunning()   {return timeout_checker_task != null && !timeout_checker_task.isDone();}


    public RouterStubManager socketFactory(SocketFactory socket_factory) {
        this.socket_factory=socket_factory;
        return this;
    }

    public RouterStubManager heartbeat(long heartbeat_interval, long heartbeat_timeout) {
        if(heartbeat_interval <= 0) {
            // disable heartbeating
            stopHeartbeatTask();
            stopTimeoutChecker();
            stubs.forEach(s -> s.handleHeartbeats(false));
            this.heartbeat_interval=0;
            return this;
        }
        if(heartbeat_interval >= heartbeat_timeout)
            throw new IllegalArgumentException(String.format("heartbeat_interval (%d) must be < than heartbeat_timeout (%d)",
                                                             heartbeat_interval, heartbeat_timeout));
        // enable heartbeating
        this.heartbeat_interval=heartbeat_interval;
        this.heartbeat_timeout=heartbeat_timeout;
        stubs.forEach(s -> s.handleHeartbeats(true));
        startHeartbeatTask();
        startTimeoutChecker();
        return this;
    }


    /**
     * Applies action to all connected RouterStubs
     */
    public void forEach(Consumer action) {
        synchronized(stubs) {
            stubs.stream().filter(RouterStub::isConnected).forEach(action);
        }
    }

    /**
     * Applies action to a randomly picked RouterStub that's connected
     * @param action
     */
    public void forAny(Consumer action) {
        RouterStub stub=findRandomConnectedStub();
        if(stub != null)
            action.accept(stub);
    }


    public RouterStub createAndRegisterStub(InetSocketAddress local, InetSocketAddress router_addr) {
        RouterStub stub=new RouterStub(local, router_addr, use_nio, this, socket_factory)
          .handleHeartbeats(heartbeat_interval > 0);
        synchronized(stubs) {
            this.stubs.add(stub);
        }
        return stub;
    }

    public RouterStub unregisterStub(InetSocketAddress router_addr_sa) {
        synchronized(stubs) {
            RouterStub s=stubs.stream().filter(st -> Objects.equals(st.remote_sa, router_addr_sa)).findFirst().orElse(null);
            if(s != null) {
                s.destroy();
                stubs.remove(s);
            }
            return s;
        }
    }


    public void connectStubs() {
        boolean failed_connect_attempts=false;
        synchronized(stubs) {
            for(RouterStub stub : stubs) {
                if(!stub.isConnected()) {
                    try {
                        stub.connect(cluster_name, local_addr, logical_name, phys_addr);
                    }
                    catch(Exception ex) {
                        failed_connect_attempts=true;
                    }
                }
            }
        }
        if(failed_connect_attempts)
            startReconnector();
    }

    
    public void disconnectStubs() {
        stopReconnector();
        synchronized(stubs) {
            for(RouterStub stub: stubs) {
                try {
                    stub.disconnect(cluster_name, local_addr);
                }
                catch(Throwable ignored) {
                }
            }
        }
    }
    
    public void destroyStubs() {
        stopReconnector();
        synchronized(stubs) {
            stubs.forEach(RouterStub::destroy);
            stubs.clear();
        }
    }

    public String printStubs() {
        return Util.printListWithDelimiter(stubs, ", ");
    }

    public String printReconnectList() {
        return stubs.stream().filter(s -> !s.isConnected())
          .map(s -> String.format("%s:%d", s.remote_sa.getHostString(), s.remote_sa.getPort()))
          .collect(Collectors.joining(", "));
    }

    public String print() {
        return String.format("Stubs: %s\nReconnect list: %s", printStubs(), printReconnectList());
    }

    public void run() {
        int failed_reconnect_attempts=0;
        synchronized(stubs) {
            for(RouterStub stub : stubs) {
                if(!stub.isConnected()) {
                    try {
                        stub.connect(this.cluster_name, this.local_addr, this.logical_name, this.phys_addr);
                        log.debug("%s: re-established connection to GossipRouter %s (group: %s)",
                                  local_addr, stub.remote(), this.cluster_name);
                    }
                    catch(Exception ex) {
                        failed_reconnect_attempts++;
                    }
                }
            }
        }
        if(failed_reconnect_attempts == 0)
            stopReconnector();
    }

    @Override
    public void closed(RouterStub stub) {
        try {
            if(log.isDebugEnabled())
                log.debug("%s: GossipRouter %s closed connection; starting reconnector task", local_addr, stub.remote());
            stub.destroy();
        }
        catch(Exception ignored) {

        }
        startReconnector();
    }


    protected synchronized void startReconnector() {
        if(reconnector_task == null || reconnector_task.isDone())
            reconnector_task=timer.scheduleWithFixedDelay(this, reconnect_interval, reconnect_interval, TimeUnit.MILLISECONDS);
    }

    protected synchronized void stopReconnector() {
        if(reconnector_task != null)
            reconnector_task.cancel(true);
    }

    protected synchronized void startHeartbeatTask() {
        if(heartbeat_task == null || heartbeat_task.isDone())
            heartbeat_task=timer.scheduleWithFixedDelay(this.send_heartbeat, heartbeat_interval, heartbeat_interval, TimeUnit.MILLISECONDS);
    }

    protected synchronized void stopHeartbeatTask() {
        stopTimeoutChecker();
        if(heartbeat_task != null)
            heartbeat_task.cancel(true);
    }

    protected synchronized void startTimeoutChecker() {
        if(timeout_checker_task == null || timeout_checker_task.isDone())
            timeout_checker_task=timer.scheduleWithFixedDelay(this.check_timeouts, heartbeat_timeout, heartbeat_timeout, TimeUnit.MILLISECONDS);
    }

    protected synchronized void stopTimeoutChecker() {
        if(timeout_checker_task != null)
            timeout_checker_task.cancel(true);
    }

    protected RouterStub findRandomConnectedStub() {
        RouterStub stub=null;
        synchronized(stubs) {
            while(connectedStubs() > 0) {
                RouterStub tmp=Util.pickRandomElement(stubs);
                if(tmp != null && tmp.isConnected())
                    return tmp;
            }
            return stub;
        }
    }

    protected void sendHeartbeat() {
        GossipData hb=new GossipData(GossipType.HEARTBEAT);
        forEach(s -> {
            try {
                s.writeRequest(hb);
            }
            catch(Exception ex) {
                log.error("failed sending heartbeat", ex);
            }
        });
    }

    protected void checkTimeouts() {
        forEach(st -> {
            long timeout=System.currentTimeMillis() - st.lastHeartbeat();
            if(timeout > heartbeat_timeout) {
                log.debug("%s: closed connection to GossipRouter %s as no heartbeat has been received for %s",
                          local_addr, st.remote(),
                          Util.printTime(timeout, TimeUnit.MILLISECONDS), st);
                st.destroy();
            }
        });
        if(disconnectedStubs())
            startReconnector();
    }

    // unsynchronized
    protected int connectedStubs() {
        return (int)stubs.stream().filter(RouterStub::isConnected).count();
    }

    public boolean disconnectedStubs() {
        synchronized(stubs) {
            return stubs.stream().anyMatch(st -> !st.isConnected());
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy