Iris through Scala... 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

class ServiceDemo extends ServiceHandler {
    // Handler initialization, invoked after successful registration
    override def init(connection: Connection) { }

    // Remaining callbacks methods, not used in this demo
    override def handleBroadcast(message: Array[Byte])              { }
    override def handleRequest(request: Array[Byte]): Array[Byte] = {return request }
    override def handleTunnel(tunnel: Tunnel)                       { }
    override def handleDrop(reason: Exception)                      { }
}

object ServiceDemo {
    def main(args: Array[String]) {
        // Register a micro-service instance into the network
        val service = new Service(55555, "Lausanne service", new ServiceDemo())

        // Unregister the service
        service.close()
    }
}

Note: Connecting as a simple client is also supported.

Service highlights

new Service(port: int, cluster: String, handler: ServiceHandler)
override def ServiceHandler::init(connection: Connection): Unit

Challenge #1 – Web requests

Simulate a system for handling web requests:

Solution #1 – Browser

// Connect to the network as a simple client
val connection = new Connection(55555)
try {
    // Issue a dummy request every second
    for (i <- 1 to 60) {
        val request = s"Request #$i".getBytes
        Try(connection.request("webserver", request, 1000)) match {
            case Success(reply) => println("Web reply: " + new String(reply))
            case Failure(error) => println("Request failed: " + error.getMessage)
        }
        Thread.sleep(1000)
    }
} finally {
    connection.close()
}

Hint: Start some webservers and check back 😉

Solution #1 – Web server

class WebServer extends ServiceHandler {
    // Generate a random ID for the web server
    val id = new Random().nextInt(100)

    // Format each request a bit and return as the reply
    override def handleRequest(request: Array[Byte]): Array[Byte] = {
        return s"scala-www-$id: ${new String(request)}".getBytes
    }
}

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

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

Request / Reply highlights

Connection::request(cluster: String, request: Array[Byte], timeout: long): Array[Byte]
override def ServiceHandler::handleRequest(request: Array[Byte]): Array[Byte]

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
val connection = new Connection(55555)
try {
    println("GLaDOS is online, sending wishes...")

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

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

Hint: Boot GLaDOS and let the experiment begin 😉

Solution #2 – Chell

// Topic subscription handler processing inbound events
class Chell extends TopicHandler {
    override def handleEvent(event: Array[Byte]) {
        println(new String(event) + "\n")
    }
}
// Connect to the Iris network as Chell
val connection = new Connection(55555)
try {
    println("Tuning in to Aperture channels...");
    connection.subscribe("official", new Chell)

    Thread.sleep(60 * 1000)
} finally {
    connection.close()
}

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

Publish / Subscribe highlights

Connection::subscribe(topic: String, handler: TopicHandler): Unit
Connection::publish(topic: String, event: Array[Byte]): Unit
override def TopicHandler::handleEvent(event: Array[Byte]): Unit

Challenge #3 – Data repository

Implement a data distribution system:

Solution #3 – Client

// Open an outbound tunnel to a data store
Try(connection.tunnel("repository", 1000)) match {
    case Failure(error) =>
        println("Tunneling failed: " + error.getMessage());

    case Success(tunnel) =>
        // Request a file and retrieve the multi-part response
        tunnel.send("some file".getBytes)

        var active = true
        while (active) {
            Try(tunnel.receive) match {
                case Success(message) => println(new String(message))
                case Failure(error)   => active = false
            }
        }
        tunnel.close
}

Hint: Start some data repositories 😉

Solution #3 – Data store

// ServiceHandler callback, invoked when a tunnel is inbound
override def handleTunnel(tunnel: Tunnel) {
    // Fetch the file name
    val name = new String(tunnel.receive())

    // Simulate sending some multi-part data stream
    for (i <- 1 to 10) {
        val part = s"Scala repo #$id: <$name> part #$i"
        tunnel.send(part.getBytes, 1000)
    }
    // Tear down the tunnel (should be in finally block)
    tunnel.close()
}

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

Tunnel highlights

Connection::tunnel(cluster: String, timeout: long): Tunnel
Tunnel::send(message: Array[Byte], timeout: long): Unit
Tunnel::receive(timeout: long): Array[Byte]

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