com.sproutsocial.nsq.ListBasedBalanceStrategy Maven / Gradle / Ivy
package com.sproutsocial.nsq;
import org.slf4j.Logger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
import static com.sproutsocial.nsq.Util.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;
public class ListBasedBalanceStrategy extends BasePubSub implements BalanceStrategy {
private static final Logger logger = getLogger(ListBasedBalanceStrategy.class);
protected final List daemonList;
private final Publisher parent;
private final Function, NsqdInstance> nsqdInstanceSelector;
private int failoverDurationSecs = 300;
/**
* Create a list based failover strategy that will alternate between all connected nsqd. Will reconnect to a
* disconnected or failed nsqd on the next publish that could be routed to that nsqd after the failoverDuration has
* expired (Default 5 min).
*
* This will throw an NSQD exception if all nsqd are in a failed state.
*
* @param nsqd a list of strings that represent HostAndPort objects.
*/
public static BiFunction getRoundRobinStrategyBuilder(List nsqd) {
return (c, p) -> buildRoundRobinStrategy(c, p, nsqd);
}
/**
* Create a list based failover strategy that shows strong preference to the first nsqd on the list.
*
* On publish, find the first nsqd in this list that is in a connected or connectable state. A nsqd is connectable
* if it has previously failed more than the configured failoverDuration (Default 5 min).
*
* This will throw an NSQD exception if all nsqd are in a failed state.
*
* @param nsqd a list of strings that represent HostAndPort objects.
*/
public static BiFunction getFailoverStrategyBuilder(List nsqd) {
return (c, p) -> buildFailoverStrategy(c,p,nsqd);
}
private static ListBasedBalanceStrategy buildRoundRobinStrategy(Client client, Publisher parent, List nsqd) {
return new ListBasedBalanceStrategy(client, parent, nsqd, new Function, NsqdInstance>() {
private int nextDaemonIndex = 0;
@Override
public NsqdInstance apply(List daemonList) {
for (int attempts = 0; attempts < daemonList.size(); attempts++) {
NsqdInstance candidate = daemonList.get(nextDaemonIndex);
boolean candidateReady = candidate.makeReady();
nextDaemonIndex++;
if (nextDaemonIndex >= daemonList.size()) {
nextDaemonIndex = 0;
}
if (candidateReady) {
return candidate;
}
}
// We've gotten to the point where all connections have been marked as 'failed'. Rather than intentionally
// dropping messages on the floor, let's at least attempt to reconnect for subsequent message publishing.
clearAllConnections(daemonList);
throw new NSQException("publish failed: Unable to establish a connection with any NSQ host: " + daemonList);
}
});
}
private static ListBasedBalanceStrategy buildFailoverStrategy(Client client, Publisher parent, List nsqd) {
return new ListBasedBalanceStrategy(client, parent, nsqd, daemonList -> {
for (int attempts = 0; attempts < daemonList.size(); attempts++) {
NsqdInstance candidate = daemonList.get(attempts);
if (candidate.makeReady()) {
return candidate;
}
}
// We've gotten to the point where all connections have been marked as 'failed'. Rather than intentionally
// dropping messages on the floor, let's at least attempt to reconnect for subsequent message publishing.
clearAllConnections(daemonList);
throw new NSQException("publish failed: Unable to establish a connection with any NSQ host: " + daemonList);
});
}
private static void clearAllConnections(final List daemonList) {
for (final NsqdInstance daemon : daemonList) {
daemon.clearConnection();
}
}
public ListBasedBalanceStrategy(Client client, Publisher parent, List nsqd, Function, NsqdInstance> nsqdInstanceSelector) {
super(client);
checkNotNull(parent);
checkNotNull(nsqd);
checkNotNull(nsqdInstanceSelector);
this.parent = parent;
this.nsqdInstanceSelector = nsqdInstanceSelector;
List nsqdInstance = new ArrayList<>();
for (String host : nsqd) {
if (host != null)
nsqdInstance.add(new NsqdInstance(host, this.parent, this.failoverDurationSecs, this));
}
daemonList = Collections.unmodifiableList(nsqdInstance);
}
@Override
public NsqdInstance getNsqdInstance() {
return nsqdInstanceSelector.apply(daemonList);
}
@Override
public synchronized void connectionClosed(PubConnection closedCon) {
for (NsqdInstance daemon : daemonList) {
if (daemon.getCon() == closedCon) {
daemon.clearConnection();
logger.debug("removed closed publisher connection:{}", closedCon.getHost());
}
}
}
@Override
public int getFailoverDurationSecs() {
return this.failoverDurationSecs;
}
@Override
public void setFailoverDurationSecs(int failoverDurationSecs) {
this.failoverDurationSecs = failoverDurationSecs;
for (NsqdInstance nsqdInstance : daemonList) {
nsqdInstance.setFailoverDurationSecs(failoverDurationSecs);
}
}
@Override
public String toString() {
return this.getClass().getSimpleName() + "{" + "daemonList=" + daemonList + ", failoverDurationSecs=" + failoverDurationSecs + '}';
}
}