Rick

Rick
Rick

Wednesday, April 13, 2016

Czar Maker is a nice set of Java reactive interfaces for Leader Election

Czar Maker

Czar Maker is a nice set of interfaces for Leader Election.
There is one Czar Maker Consul implementation of this interface that uses Consul. You could use the interface to implement leader election with zookeeper or etcd. Consul and etcd use the RAFT algorithm to present a reliable kv storage (Zookeeper uses a similar technique as Consul and etcd).
Czar uses Reakt, a Java reactive, streaming API, with callbacks and promises that is Java 8 and Lambda friendly
Czar also uses QBit microservices as its HTTP/IO lib.

Getting Started

This library is just interfaces, to use Czar on your project you will need the Czar Maker Consul implementation.

maven

<dependency>
    <groupId>io.advantageous.czarmaker</groupId>
    <artifactId>czar-maker</artifactId>
    <version>0.1.0.RELEASE</version>
</dependency>

gradle

compile 'io.advantageous.czarmaker:czar-maker:0.1.0.RELEASE'

Example usage

public class MyService {

    private final String host;
    private final int port;
    private final LeaderElector leaderElector;
    private AtomicBoolean amILeader = new AtomicBoolean();
    private AtomicReference<Endpoint> leaderEndpoint = new AtomicReference<>();

    public void init() {
            Promise<Endpoint> getLeaderPromise = Promises.<Endpoint>promise();

            /* Call elect new leader. */
            getLeaderPromise.thenExpect(expected ->
                    expected
                            .ifEmpty(this::nominateSelf)
                            .ifPresent(endpoint -> leaderEndpoint.set(endpoint)))
                            .catchError(Throwable::printStackTrace);
            leaderElector.getLeader(getLeaderPromise);

            /* Register for stream of leadership changes. */
            registerForLeadershipNotices();

    }


  private void registerForLeadershipNotices() {
        leaderElector.leadershipChangeNotice(result ->
                result
                        .thenExpect(this::checkIfThisServiceIsLeader)
                        .catchError(Throwable::printStackTrace)
        );
    }

    //Handles leadership change stream. 
    private void checkIfThisServiceIsLeader(Expected<Endpoint> expectedEndpoint) {
        expectedEndpoint.ifEmpty(() -> nominateSelf()) //If empty then nominate this service
                .ifPresent(endpoint -> {
                    amILeader.set(endpoint.getHost().equals(host) && endpoint.getPort()==port);
                    leaderEndpoint.set(endpoint);
                });
    }

    //Attempt to Nominate self if there is no leader
    private void nominateSelf() {
        final Promise<Boolean> selfElectPromise = Promises.<Boolean>promise();
        selfElectPromise.then((elected) -> {
            amILeader.set(elected);
        }).catchError(Throwable::printStackTrace);

        leaderElector.selfElect(new Endpoint(host, port), selfElectPromise);

    }

    public boolean isLeader() {
        return amILeader.get();
    }

    public Endpoint getLeaderEndpoint() {
        return leaderEndpoint.get();
    }

Related projects

Kafka and Cassandra support, training for AWS EC2 Cassandra 3.0 Training