xKoon

JSON-based Reactive Service Framework

Presentation

This library provides a small framework to build network services (servers & clients) around a very basic JSON message format, using RxIO.

Installation

Gemfile

gem 'xkoon'

Terminal

gem install -V xkoon

Usage

Building a service endpoint (whether client or server) with xKoon requires the following:

  1. define a Processor module (for your business logic) providing a bunch of process_XXX methods'

  2. instantiate an XKoon::Service or XKoon::Client object around your processor

  3. run it

The Processor

The processor is at the heart of xKoon - it provides all the necessary abstractions for building simple services.

How to use

To build a processor, simply include XKoon::Processor into any module. Then, any method in your module that starts with process_ will be made available to remote peers.

module ExampleProcessor
    include XKoon::Processor

    def process_foobar p, h, m
        puts "Got a 'foobar' request: peer=#{p} handler=#{h} message=#{m}"
    end
end

Sending requests

The send_request method in XKoon::Processor allows sending requests to remote peers.

Processor.send_request p, h, m, :some_request_method, data: { foo: :bar }

Sending responses

While processing a request, a response can be sent back using the send_response method in XKoon::Processor.

# Success
send_response p, h, m, 'Everything was processed correctly!'

Errors should be sent using the send_error method.

# Failure
send_error p, h, m, 'Something went wrong :('

Error Wrapper

An error wrapper is provided within XKoon::Processor, allowing for a more transparent implementation of business logic.

The error_wrapper method will yield any block passed to it, rescuing any exception and immediately throwing it back to the remote peer.

error_wrapper p, h, m do
    raise "This will get thrown back to remote peer"
end

Dealing with responses

Any response to a request will be handed to the handle_response method of the processor. By default the method is empty. You may override it to implement any custom behavior.

module ExampleProcessor
    include XKoon::Processor

    def handle_response p, h, m
        puts "Got a response from peer [#{p}]: #{m}"
    end
end

Client / Server Processors

The processor does not care about being the client or server. This means you can implement a single processor serving as a two-way module for both client & server.

Request Format

xKoon Requests contain the following information:

{
    # Requested Method
    req: :some_request_method,

    # Timestamp marking the time at which the remote peer sent the request
    sent: 1495623525
}

Method Proxying (Make everything transparent)

xKoon injects a to_proxy method into every Peer Hash. This method simply creates a Proxy around the peer and returns it.

The proxy object serves as a remote method interface: any method called on it will be forwarded to the remote peer.

# Create Proxy around Peer
pxy = peer_hash.to_proxy

# Send a 'foobar' request to the remote peer
pxy.foobar hello: :world

Response Format

xKoon Responses contain the following information:

{
    # Success (true / false)
    success: true,

    # Original Request
    req: { req: :some_request_method, sent: 1495623525 },

    # Whatever the remote peer responded (can be any object - will be converted to JSON)
    res: 'Got your message!',

    # Timestamp marking the time at which the remote peer sent the response
    sent: 1495623525
}

Example

Below is a simple but complete example presenting an 'echo' service, replying back anything that was requested.

require 'xkoon'

# Proxy objects
PXY = {}

# Response Objects
RES = {}

# Two-Way Processor (used by both the client & server)
module EchoProcessor
    include XKoon::Processor

    # On Join (Attach the Proxy Objects as soon as we're online)
    def self.on_join peer, handler
        PXY[:server] = peer.to_proxy if peer.has_key? :serv
        PXY[:client] = peer.to_proxy if peer.has_key? :client
    end

    # On Drop (Release the Proxy Objects whenever we lose the connection)
    def self.on_drop peer, handler
        PXY[:server] = nil if peer.has_key? :serv
        PXY[:client] = nil if peer.has_key? :client
    end

    # Process 'echo' Request
    def self.process_echo p, h, m
        puts "Got echo request: #{m[:data]}"
        send_response p, h, m, m[:data]
    end

    # Handle Response
    def self.handle_response p, h, m
        RES[:server] = m if p.has_key? :serv
        RES[:client] = m if p.has_key? :client
        puts "Got response from peer #{p[:peer][:name]}:#{p[:peer][:port]}: #{m[:res]}"
    end
end

# Create Endpoints
srv = XKoon::Service.new EchoProcessor
cli = XKoon::Client.new EchoProcessor

# Start Server & Client
srv.startup
cli.startup

# Wait for client and echo something
sleep 0.1 until PXY[:client]
PXY[:client].echo data: { str: 'hello world!' }

# Wait for Response to come back
sleep 0.1 until RES[:client]

# Drop Client and wait for drop
cli.shutdown
sleep 0.1 while PXY[:server]

# Drop Server
srv.shutdown

License

The gem is available as open source under the terms of the MIT License.