orbit

module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 9, 2020 License: MIT

README

GoDoc coverage license

Orbit provides a powerful backend to interlink remote applications with each other.
It replaces connectionless RPC solutions with a multiplexed session-based solution that includes convenient features (such as raw streams, signals and more ...).

Orbit generally does not expect you to use a strict client-server architecture. Both peers can call functions and trigger signals at the other peer. Therefore, it is possible to implement both client-server and peer-to-peer applications.

Features
  • Session-based
  • Multiplexed connections with multiple channel streams and Keep-Alive (using yamux)
  • Plugable custom codecs for encoding and decoding (defaulting to MessagePack using msgpack)
  • Use raw tcp streams to implement your own protocols, and/or
    • Use the control package for RPC-like approaches
    • Use efficient signals for event-based approaches
  • Provide an authentication hook to easily authenticate your peers.
  • Configure everything to your needs, such as:
    • your preferred logger
    • allowed message sizes
    • timeouts
    • ...
  • Easy setup (check out the sample)
Control - RPC

The control package provides an implementation for Remote Procedure Calls. This allows a peer to define functions that other connected peers can then call.

Setup

First, you need to setup a control on each peer. It is best to use either the provided Init or InitMany functions to do this.

ctrl, _, err := orbitSession.Init(&orbit.Init{
    Control: orbit.InitControl{
        Funcs: map[string]control.Func{
            api.Action1: handleAction1,
        },
    },
})
if err != nil {
    return err
}

ctrl.Ready()

To use a custom initialization, check out the source code of the InitMany function to get a grasp of what needs to be done.

Synchronous Call

To make a synchronous call, simply trigger a call on peer1 to peer2 and then wait for the incoming response.

type Action1Args struct {
    ID int
}

type Action1Ret struct {
    SomeData string
}

func Action1() (data string, err error) {
    // Call
    ctx, err := s.ctrl.Call("Action1", &Action1Args{
        ID: 28
    })
    if err != nil {
        return
    }
    
    // Response
    var response api.Action1Ret
    err = ctx.Decode(&response)
    if err != nil {
        return
    }
    
    data = response.SomeData
    return 
}

peer2 might handle this call like this:

func handleAction1(ctx *control.Context) (v interface{}, err error) {
    var args Action1Args
    err = ctx.Decode(&args)
    if err != nil {
        return
    }
    
    // handle the request ...
    
    v = &Action1Ret{
        SomeData: someData,
    }
    return
}

Note, that the handleAction1 func of peer2 must have been added to the control of peer2 for the correct key. Check out the Control Setup to see how to do this

Asynchronous Call

An asynchronous call is very similar to its synchronous counterpart.

type Action2Args struct {
    ID int
}

type Action2Ret struct {
    SomeData string
}

// Call Async
func Action2() error {
    callback := func(data interface{}, err error) {
        if err != nil {
            log.Fatal(err)
        }
        
        // Response
        var response Action2Ret
        err = ctx.Decode(&response)
        if err != nil {
            return
        }
        
        // handle data...
        println(response.SomeData)
    }
    
    return s.ctrl.CallAsync(
        "Action2", 
        &Action2Args{
            ID: 28,
        }, 
        callback,
    )
}

Inside the callback, you receive the response (or an error) and can handle it the same way as with the synchronous call.

Signaler - Events

The signaler package provides an implementation for sending events to remote peers. Under the hood, it uses the control package's CallOneWay function tFirst, you need to setup a control on each peer. It is best to use either the provided Init or InitMany functions to do this.o make calls without expecting a response.

The signaler package adds a lot of convenient stuff, such as allowing peers to set filters on their events, or unregister from an event completely.

The code in the following sections is taken from the sample.

Setup

First, you need to setup a signaler on each peer. It is best to use either the provided Init or InitMany functions to do this.

We start with the peer that emits the signal, the sender:

// Initialize the signaler and declare, which signals
// can be triggered on it.
_, sig, err := orbitSesion.Init(&orbit.Init{
    Signaler: orbit.InitSignaler{
        Signals: []orbit.InitSignal{
            {
                ID: "TimeBomb",
            },
        },
    },
})
if err != nil {
    return
}

// Start the signaler.
sig.Ready()

First, we initialize an orbit session using Init and we register a signaler on it that can emit the "TimeBomb" signal.
If this succeeds, we start the signaler by calling its Ready() method, which starts the listen routines of the signaler.

Now let us move on to the peer that receives the signal, the receiver:

// Initialize the signaler and declare, which signals
// can be triggered on it.
_, sig, err := orbitSesion.Init(&orbit.Init{
    Signaler: orbit.InitSignaler{
        Signals: []orbit.InitSignal{},
    },
})
if err != nil {
    return
}

// Register handlers for events from the remote peer
_ = sig.OnSignalFunc("TimeBomb", onEventTimeBomb)

// Start the signaler.
sig.Ready()

Again, we need to initialize the signaler for this peer as well, however, we do not register any signals on it, since we only want to receive signals from the remote peer right now.
Afterwards, we register a handler func for the "TimeBomb" signal, the onEventTimeBomb function.
In the end, we start the signaler.

Here is the implementation of the onEventTimeBomb handler func:

func onEventTimeBomb(ctx *signaler.Context) {
	var args api.TimeBombData
	err := ctx.Decode(&args)
	if err != nil {
		log.Printf("onEventTimeBomb error: %v", err)
		return
	}

	// Do something with the signal data...
}

It is identical to the control handler funcs, only that we do not return something to the caller, as signals are unidirectional.

Now, if we want to finally trigger our signal on the sender, we can do it like this:

// Trigger the event.
args := &api.TimeBombData{
    Countdown: 5,
}

err = sig.TriggerSignal("TimeBomb", &args)
if err != nil {
    log.Printf("triggerSignal TimeBomb: %v", err)
    return
}

We call the TriggerSignal method on our signaler we defined at the beginning of this section. This sends the given arguments over the wire to our receiver, where the onEventTimeBomb handler func will be triggered.

Directories

Path Synopsis
_unused
api
Package api contains types that are internally used to send data via the control and signaler pkg.
Package api contains types that are internally used to send data via the control and signaler pkg.
codec
Package codec contains sub-packages with different codecs that can be used to encode/decode any entity to/from a byte stream.
Package codec contains sub-packages with different codecs that can be used to encode/decode any entity to/from a byte stream.
codec/json
Package json offers an implementation of the codec.Codec interface for the json data format.
Package json offers an implementation of the codec.Codec interface for the json data format.
codec/msgpack
Package msgpack offers an implementation of the codec.Codec interface for the msgpack data format.
Package msgpack offers an implementation of the codec.Codec interface for the msgpack data format.
control
Package control provides an implementation of a simple network protocol that offers a RPC-like request/response mechanism between two peers.
Package control provides an implementation of a simple network protocol that offers a RPC-like request/response mechanism between two peers.
flusher
Package flusher provides convenience methods to flush a net.Conn.
Package flusher provides convenience methods to flush a net.Conn.
signaler
* ORBIT - Interlink Remote Applications * * The MIT License (MIT) * * Copyright (c) 2018 Roland Singer <roland.singer[at]desertbit.com> * Copyright (c) 2018 Sebastian Borchers <sebastian[at]desertbit.com> * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software.
* ORBIT - Interlink Remote Applications * * The MIT License (MIT) * * Copyright (c) 2018 Roland Singer <roland.singer[at]desertbit.com> * Copyright (c) 2018 Sebastian Borchers <sebastian[at]desertbit.com> * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software.
cmd
examples
simple/hello
code generated by orbit
code generated by orbit
internal
api
bytes
Package bytes offers convenience functions to convert bytes to and from unsigned integers, respecting a defined byte-order.
Package bytes offers convenience functions to convert bytes to and from unsigned integers, respecting a defined byte-order.
flusher
Package flusher provides convenience methods to flush a net.Conn.
Package flusher provides convenience methods to flush a net.Conn.
utils
Package utils is the common sin of every Go programmer, including functions that seem to be usable everywhere, but do not share the same functionality.
Package utils is the common sin of every Go programmer, including functions that seem to be usable everywhere, but do not share the same functionality.
pkg
codec
Package codec contains sub-packages with different codecs that can be used to encode/decode any entity to/from a byte stream.
Package codec contains sub-packages with different codecs that can be used to encode/decode any entity to/from a byte stream.
codec/json
Package json offers an implementation of the codec.Codec interface for the json data format.
Package json offers an implementation of the codec.Codec interface for the json data format.
codec/msgpack
Package msgpack offers an implementation of the codec.Codec interface for the msgpack data format.
Package msgpack offers an implementation of the codec.Codec interface for the msgpack data format.
packet
Package packet provides convenience methods to read/write packets to a net.Conn.
Package packet provides convenience methods to read/write packets to a net.Conn.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL