Iris through Java... API v1

Bringing simplicity back to distributed services

Péter Szilágyi

European Institute for Innovation and Technology

Eötvös Loránd University, Budapest, Hungary

Babeş-Bolyai University, Cluj-Napoca, Romania

Note, these are the offline slides of the presentation. For executable codes, please check playground availability at http://iris.karalabe.com/talks.

What the heck is Iris?

Decentralized cloud messaging

I.e. If starting a batch of VMs on Google Compute Engine is a one liner,

gcutil addinstance vm-1 vm-2 ... vm-N

Assembling them into a distributed system should be a one liner too!

iris -net <service name> -rsa <private key>

What can it do?

It communicates, of course!

What makes it special? Simplicity!

Instances consolidated based on responsibility

Clusters are the smallest logical units

⊕ Harder to abuse, fewer moving components, self organization

Meaningful and non-dynamic addressing

Routing based on semantic addressing

⊕ Implicit failovers, automatic load balancing, simpler client code

Implicit security while trusting the trustworthy

Security at service level

⊕ Decoupled system, optimized encryption, out of the box

Show me the code!

Challenge #0 – Boilerplate

Assemble a network of micro services:

Solution #0 – Service

public class ServiceDemo implements ServiceHandler {
    // Handler initialization, invoked after successful registration
    @Override public void init(Connection connection) throws InitializationException { }

    public static void main(String args[]) {
        // Register a micro-service instance into the network
        try (Service service = new Service(55555, "Javatar service", new ServiceDemo())) {
            // Do something, before tearing down
        }
        catch (IOException | InitializationException | InterruptedException e) {
            throw new RuntimeException("Failed to register the service", e);
        }
    }

    // Remaining callbacks methods, not used in this demo
    @Override public void handleBroadcast(byte[] message)                                { }
    @Override public byte[] handleRequest(byte[] req) throws RemoteException { return req; }
    @Override public void handleTunnel(Tunnel tunnel)                                    { }
    @Override public void handleDrop(Exception reason)                                   { }
}

Note: Connecting as a simple client is also supported.

Service highlights

new Service(int port, String cluster, ServiceHandler handler)
@Override void ServiceHandler::init(Connection connection)

Challenge #1 – Web requests

Simulate a system for handling web requests:

Solution #1 – Browser

// Connect to the network as a simple client
try (Connection connection = new Connection(55555)) {
    // Issue a dummy request every second
    for (int i = 1; i <= 60; i++) {
        byte[] request = ("Request #" + i).getBytes();

        try {
            byte[] reply = connection.request("webserver", request, 1000);
            System.out.println("Web reply: " + new String(reply));
        } catch (Exception e) {
            System.out.println("Request failed: " + e.getMessage());
        }
        Thread.sleep(1000);
    }
}

Hint: Start some webservers and check back 😉

Solution #1 – Web server

class WebServer implements ServiceHandler {
    // Generate a random ID for the web server
    final int id = new Random().nextInt(100);

    // Format each request a bit and return as the reply
    @Override public byte[] handleRequest(byte[] request) throws RemoteException {
        return ("java-www-" + id + ": " + new String(request)).getBytes();
    }
}

// Register a webserver micro-service into the network
try (Service service = new Service(55555, "webserver", new WebServer())) {
    System.out.println("Waiting for inbound requests...");
    Thread.sleep(60 * 1000);
}

The presentation supports only one active demo process per window. Open new tab?

Request / Reply highlights

byte[] Connection::request(String cluster, byte[] request, long timeout)
@Override byte[] ServiceHandler::handleRequest(byte[] request)

Challenge #2 – Aperture Science Enrichment Center 😈

Implement the comlink for Aperture Laboratories¹:

¹ http://en.wikipedia.org/wiki/Portal_(video_game)

Solution #2 – GLaDOS

// Connect to the Iris network as GLaDOS
try (Connection connection = new Connection(55555)) {
    System.out.println("GLaDOS is online, sending wishes...");

    while (true) {
        // Pick a random wish from hidden 'wishes' array
        String wish = wishes[new Random().nextInt(wishes.length)];

        connection.publish("official", ("GLaDOS: " + wish).getBytes());
        Thread.sleep(5000);
    }
}

Hint: Boot GLaDOS and let the experiment begin 😉

Solution #2 – Chell

// Topic subscription handler processing inbound events
class Chell implements TopicHandler {
    @Override public void handleEvent(byte[] event) {
        System.out.println(new String(event) + "\n");
    }
}
// Connect to the Iris network as Chell
try (Connection connection = new Connection(55555)) {
    System.out.println("Tuning in to Aperture channels...");
    connection.subscribe("official", new Chell());

    Thread.sleep(60 * 1000);
}

Hint: Maybe there is an "unofficial" channel? 😉

Publish / Subscribe highlights

void Connection::subscribe(String topic, TopicHandler handler)
void Connection::publish(String topic, byte[] event)
@Override void TopicHandler::handleEvent(byte[] event)

Challenge #3 – Data repository

Implement a data distribution system:

Solution #3 – Client

// Open an outbound tunnel to a data store
try (Tunnel tunnel = connection.tunnel("repository", 1000)) {
    // Request a file and retrieve the multi-part response
    tunnel.send("some file".getBytes());

    while (true) {
        try {
            byte[] part = tunnel.receive();
            System.out.println(new String(part));
        } catch (Exception e) {
            break;
        }
    }
} catch (Exception e) {
    System.out.println("Tunneling failed: " + e.getMessage());
}

Hint: Start some data repositories 😉

Solution #3 – Data store

// ServiceHandler callback, invoked when a tunnel is inbound
@Override public void handleTunnel(Tunnel tunnel) {
    try {
        // Fetch the file name
        String name = new String(tunnel.receive());

        // Simulate sending some multi-part data stream
        for (int i = 1; i <= 10; i++) {
            String part = String.format("Java repo #%d: <%s> part #%d", id, name, i);
            tunnel.send(part.getBytes(), 1000);
        }
        // Tear down the tunnel (should be in finally block)
        tunnel.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

The presentation supports only one active demo process per window. Open new tab?

Tunnel highlights

Tunnel Connection::tunnel(String cluster, long timeout)
void Tunnel::send(byte[] message, long timeout)
byte[] Tunnel::receive(long timeout)

How does this all work?

Sneak behind the scenes

Iris nodes do the heavy lifting (one/host):

Thin clients bathe in the glory:

Where to go next?

Iris resources

Iris community:

Thank you

Péter Szilágyi

European Institute for Innovation and Technology

Eötvös Loránd University, Budapest, Hungary

Babeş-Bolyai University, Cluj-Napoca, Romania