Package org.projectx.zookeeper.election

Source Code of org.projectx.zookeeper.election.LeaderElectorImpl

package org.projectx.zookeeper.election;

import java.util.NavigableSet;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.IZkStateListener;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import org.projectx.zookeeper.LeaderElectionNodeListener;
import org.projectx.zookeeper.SequentialZNode;
import org.projectx.zookeeper.ZookeeperOperations;

/**
* An implementation of the {@link LeaderElector} interface based on a Zookeeper
* DAO. Maintains the state of affairs by holding an atomic reference of the
* current {@link ElectionState}. Notifies the execution target of changes in
* the state of leadership via the provided {@link LeaderElectionTarget}
* instance.
*
* @author Erez Mazor (erezmazor@gmail.com)
*/
public class LeaderElectorImpl implements LeaderElector, LeaderElectionNodeListener {

  private static final Logger log = LoggerFactory.getLogger(LeaderElectorImpl.class);

  private final ZookeeperOperations zkDao;
  private final LeaderElectionTarget leaderElectionTarget;
  private LeaderElectionStrategy electionStrategy = new AntiHerdingElectionStrategy();

  private final ElectionStateFactory electionStateFactory;

  private final AtomicReference<ElectionState> electionState = new AtomicReference<ElectionState>();

  private DateTime lastElection;

  private final AtomicBoolean connected = new AtomicBoolean(false);

  private LeaderElectionListener electionListener = new LoggingLeaderElectionListener();

  private final boolean disabled;

  public LeaderElectorImpl(final ZookeeperOperations zkDao,
      final LeaderElectionTarget leaderElectionTarget,
      final ElectionStateFactory electionStateFactory, final boolean disabled) {
    Assert.notNull(zkDao, "zkDao cannot be null");
    Assert.notNull(leaderElectionTarget, "leaderElectionTarget cannot be null");
    Assert.notNull(electionStateFactory, "electionStateFactory cannot be null");
    this.zkDao = zkDao;
    this.leaderElectionTarget = leaderElectionTarget;
    this.electionStateFactory = electionStateFactory;
    this.disabled = disabled;
  }

  @Override
  public void setElectionStrategy(final LeaderElectionStrategy electionStrategy) {
    this.electionStrategy = electionStrategy;
  }

  public void setLeaderElectionListener(final LeaderElectionListener electionListener) {
    this.electionListener = electionListener;
  }

  /**
   * Checks that the state of nodes in zookeeper is valid, then checks that the
   * leader is alive if it isn't alive checks if this instance is the current
   * leader and if it is starts the execution target.
   */
  @Override
  public void electNewLeader() {
    if (disabled) {
      return;
    }

    final NavigableSet<SequentialZNode> nodes = getValidChildren();
    final boolean leaderAlive = zkDao.nodeExists(getLeader().getFullPath());
    if (!leaderAlive) {
      SequentialZNode leader = getLeader();
      log.info("Leader node {} is not alive!", getLeader());
      leader = electionStrategy.findNextLeader(nodes, getNode());

      electionState.set(getState().valueOf(leader));

      log.info("Registering a listener for leader node {}", leader);
      registerLeaderListener(leader.getFullPath());
    }

    if (electionStrategy.isLeader(nodes, getNode()) && !leaderElectionTarget.isRunning()) {
      log.info("My node {}, is the elected leader", getNode());
      leaderElectionTarget.start();
      electionListener.handleNewLeader(getLeader());
    }

    log.info("Local node is: {}, leader node: {}", getNode(), getLeader());
    lastElection = new DateTime();
    connected.set(true);
  }

  /**
   * Gets a list of child nodes and verifies that the list is correct state
   * (i.e., list is not empty and self node exists in the list). If the list is
   * in illegal state, stops the election target to be safe and re-initializes
   * the self node state.
   *
   * @return
   */
  private NavigableSet<SequentialZNode> getValidChildren() {
    final NavigableSet<SequentialZNode> nodes = getChildren();
    if (null == nodes || !nodes.contains(getNode())) {
      log.warn("Found a null list of child nodes or self node doesn't exist in node list, recreating self node.");
      leaderElectionTarget.stop();
      initialize();
      return getChildren();
    }

    return nodes;
  }

  /**
   * Get a list of child nodes from zookeeper
   *
   * @return
   */
  private NavigableSet<SequentialZNode> getChildren() {
    final String rootPath = getNode().getPath();
    final NavigableSet<SequentialZNode> nodes = zkDao.findChildren(rootPath);
    log.info("Election Zookeeper nodes: {}", StringUtils.collectionToCommaDelimitedString(nodes));
    return nodes;
  }

  @Override
  public void disconnected() {
    leaderElectionTarget.stop();
    connected.set(false);
  }

  @Override
  public void initialize() {
    if (disabled) {
      return;
    }

    final ElectionState initialState = electionStateFactory.create();
    electionState.set(initialState);
    registerLeaderListener(initialState.getLeader().getFullPath());
  }

  private void registerLeaderListener(final String leaderPath) {
    zkDao.registerListener(leaderPath, this);
  }

  @Override
  public ElectionState getState() {
    return electionState.get();
  }

  @Override
  public boolean isConnected() {
    return connected.get();
  }

  @Override
  public boolean isLeader() {
    final ElectionState state = getState();
    return state.getLeader().equals(state.getNode());
  }

  @Override
  public SequentialZNode getNode() {
    return getState().getNode();
  }

  @Override
  public SequentialZNode getLeader() {
    return getState().getLeader();
  }

  @Override
  public DateTime getLastElectionTime() {
    return lastElection;
  }

  /**
   * An {@link IZkStateListener} implementation used to notify the leader
   * election framework about disconnects and reconnects from the Zookeeper
   * ensemble. The ZkClient handles reconnects on its own. However, when a
   * session has expired or disconnected the
   * {@link LeaderElectorListenerCallback} must be notified in order for work to
   * be stopped (or any other action the callback wishes to implement for this
   * scenario).
   *
   *
   * @author Erez Mazor (erezmazor@gmail.com)   *
   */
  @Override
  public void handleStateChanged(final KeeperState state) throws Exception {
    switch (state) {
    case Disconnected:
      log.info("Zookeeper session Disconnected, this will cause the target execution to stop.");
      disconnected();
      break;
    case Expired:
      log.info("Zookeeper session Expired, this will cause the target execution to stop, and possibly for new nodes to be created.");
      disconnected();
      break;
    case SyncConnected:
      log.info("Zookeeper session SyncConnected. checking with leaderElector if a new election is required.");
      if (!isConnected()) {
        connected.set(true);
        electNewLeader();
      }
      break;

    }
  }

  @Override
  public void handleNewSession() throws Exception {
    log.warn("Zookeeper signalled a new session, this means that all ephemeral nodes have been removed, re-initializing session.");
    initialize();
  }

  @Override
  public void handleDataChange(final String dataPath, final Object data) throws Exception {

  }

  /**
   * {@link IZkDataListener} implementation intended to notify the Leader
   * Election framework about node deletions. This represents the primary entry
   * point trigger for a new election since a deletion of a leader node means
   * the next follower in line must assume leadership.
   * <p>
   *
   */
  @Override
  public void handleDataDeleted(final String dataPath) throws Exception {
    final SequentialZNode leader = getLeader();

    if (dataPath.equals(leader.getFullPath())) {
      log.info("Detected leader node deletion at: {}", dataPath);
      electNewLeader();
    }

  }
}
TOP

Related Classes of org.projectx.zookeeper.election.LeaderElectorImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.