armie

package module
v0.0.0-...-e5db901 Latest Latest
Warning

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

Go to latest
Published: Aug 13, 2019 License: BSD-2-Clause Imports: 12 Imported by: 0

README

Armie

Async-RMI and Eventing framework.

Package armie provides a framework for async, symmetric RMI and Eventing. Connections facilitate both Requests (with responses) and Events (no response). Requests are issued asynchronously, and a single connection can have any number of outstanding requests (a map of request-ids is kept in order to route responses). Issuing a request returns a Future that can be used to acquire a response.

A single Armie connection can process tens of thousands of requests-per-second.

Example:

type Person struct {
	Name string
	Age  int
}

func sayHello(person *Person) int {
	fmt.Printf("%s says 'Hello'.\n", person.Name)
	return person.Age
}

var handleReq armie.RequestHandler = func(request *armie.Request, response *armie.Response) {
	switch request.Method {
	case "HELLO":
		age, err := request.CallMethod(sayHello)
		if err != nil {
			response.Error(err.Error())
		} else {
			response.Send(age)
		}
	}
}

var handleEvt armie.EventHandler = func(event *armie.Event) {
	switch event.Event {
	case "ARRIVED":
		var name string
		event.Decode(&name)
		fmt.Printf("%s has arrived.\n", name)
	}
}

func main() {
	s := armie.NewTCPServer(os.Stdout)

	// ConnectionHandler should wire up handlers on each
	// new connection to the server
	s.OnConnection(func(conn *armie.Conn) error {
		conn.OnRequest(handleReq)
		conn.OnEvent(handleEvt)
		return nil
	})


	err := s.Listen(":9999")
	if err != nil {
		fmt.Printf("Error setting up server: %s", err.Error())
	}

	// The client connection can optionally accept a connectionHandler,
	// or Request / Event Handlers can be wired up later with OnRequest
	// and OnEvent.  The former method is necessary if you expect a request
	// or event to be waiting on the wire immediately.
	conn, err := armie.NewTCPConnection(":9999", os.Stdout, nil)
	if err != nil {
		fmt.Printf("Error connecting: %s", err.Error())
	}

	joe := Person{
		Name: "Joe",
		Age: 30,
	}
	conn.SendEvent("ARRIVED", "Joe")

	res, err := conn.SendRequest("HELLO", &joe)
	var joesAge int
	res.GetResult(&joesAge)
	fmt.Printf("Joe is %d.\n", joesAge)
}

Output:

Joe has arrived.
Joe says 'Hello'.
Joe is 30.

Documentation

Overview

Armie

Async-RMI and Eventing framework.

Package armie provides a framework for async, symmetric RMI and Eventing. Connections facilitate both Requests (with responses) and Events (no response). Requests are issued asynchronously, and a single connection can have any number of outstanding requests (a map of request-ids is kept in order to route responses). Issuing a request returns a Future that can be used to acquire a response.

Example:

type Person struct {
	Name string
	Age  int
}

func sayHello(person *Person) int {
	fmt.Printf("%s says 'Hello'.\n", person.Name)
	return person.Age
}

var handleReq armie.RequestHandler = func(request *armie.Request, response *armie.Response) {
	switch request.Method {
	case "HELLO":
		age, err := request.CallMethod(sayHello)
		if err != nil {
			response.Error(err.Error())
		} else {
			response.Send(age)
		}
	}
}

var handleEvt armie.EventHandler = func(event *armie.Event) {
	switch event.Event {
	case "ARRIVED":
		var name string
		event.Decode(&name)
		fmt.Printf("%s has arrived.\n", name)
	}
}

func main() {
	s := armie.NewTCPServer(os.Stdout)

	// ConnectionHandler should wire up handlers on each
	// new connection to the server
	s.OnConnection(func(conn *armie.Conn) error {
		conn.OnRequest(handleReq)
		conn.OnEvent(handleEvt)
		return nil
	})

	err := s.Listen(":9999")
	if err != nil {
		fmt.Printf("Error setting up server: %s", err.Error())
	}

	// The client connection can optionally accept a connectionHandler,
	// or Request / Event Handlers can be wired up later with OnRequest
	// and OnEvent.  The former method is necessary if you expect a request
	// or event to be waiting on the wire immediately.
	conn, err := armie.NewTCPConnection(":9999", os.Stdout, nil)
	if err != nil {
		fmt.Printf("Error connecting: %s", err.Error())
	}

	joe := Person{
		Name: "Joe",
		Age: 30,
	}
	conn.SendEvent("ARRIVED", "Joe")

	res, err := conn.SendRequest("HELLO", &joe)
	var joesAge int
	res.GetResult(&joesAge)
	fmt.Printf("Joe is %d.\n", joesAge)
}

Output:

Joe has arrived.
Joe says 'Hello'.
Joe is 30.

Index

Constants

View Source
const (
	REQUEST = iota + 1
	RESPONSE
	EVENT
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Conn

type Conn struct {
	Alive bool
	// contains filtered or unexported fields
}

Conn is Symmetric. Both server and client can register RequestHandlers and EventHandlers, and both can SendEvent() and SendRequest().

func NewTCPConnection

func NewTCPConnection(addr string, logout io.Writer, handler ConnectionHandler) (*Conn, error)

func (*Conn) Close

func (c *Conn) Close() error

Close the connection. Close will not return until the goroutine reading events and requests exits.

func (*Conn) OnEvent

func (c *Conn) OnEvent(handler EventHandler)

Register an event handler.

func (*Conn) OnRequest

func (c *Conn) OnRequest(handler RequestHandler)

Register a request handler.

func (*Conn) SendEvent

func (c *Conn) SendEvent(method string, data interface{}) error

Send an asynchronous Event. Events should have an event name, and a single data object. Events are one-way communications and there's no guarantee they arrive if the connection is lost.

func (*Conn) SendRequest

func (c *Conn) SendRequest(method string, args ...interface{}) (*Future, error)

Send an asynchronous RMI Request. Multiple arguments are supported. Returns a Future that can be used to await the result.

type ConnectionHandler

type ConnectionHandler func(conn *Conn) error

type Event

type Event struct {
	Event   string
	Payload []byte
}

An event containing an event name and an encoded payload.

func (*Event) Decode

func (e *Event) Decode(v interface{}) error

type EventHandler

type EventHandler func(event *Event)

type Future

type Future struct {
	// contains filtered or unexported fields
}

RPC future for awaiting responses to RMI requests

func (*Future) GetResult

func (f *Future) GetResult(res interface{}) error

Await the response or error. If the error returned is not-nil, the result will be nil.

type Request

type Request struct {
	Method  string
	Id      uint64
	Payload []byte
}

An RMI request containing encoded arguments and a method name

func (*Request) CallMethod

func (r *Request) CallMethod(method interface{}) (interface{}, error)

Decode request arguments to match the method args, and call the method. The method can return an error and, at most one other object, which will in-turn be returned by CallMethod.

func (*Request) DecodeArgs

func (r *Request) DecodeArgs(types []reflect.Type) ([]interface{}, error)

Decode arguments using reflect.Types. In most cases (true RMI) CallMethod() will be more convenient (it uses decodeArgs internally base on the target method arguments)

type RequestHandler

type RequestHandler func(request *Request, response *Response)

type Response

type Response struct {
	Id        uint64
	ErrString string
	Result    interface{}
	// contains filtered or unexported fields
}

Response is passed to a RequestHandler to allow the RequestHandler to send a result or an error.

func (*Response) Error

func (r *Response) Error(err string) error

Send the given error. After an error is sent, the Response is no longer usable.

func (*Response) Send

func (r *Response) Send(result interface{}) error

Send the given result. After a result is sent, the Response is no longer usable.

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server provides a Listen(addr) method for accepting new connections. Register a ConnectionHandler with OnConnection() and use the ConnectionHandler to wire up a Request and / or Event handler, and do any other housekeeping required before requests and events are accepted.

func NewTCPServer

func NewTCPServer(logout io.Writer) *Server

func (*Server) Close

func (serv *Server) Close() error

Stop listening. Close will not return until listener goroutine has exited.

func (*Server) Listen

func (serv *Server) Listen(addr string) error

Listen at the given address for new connections. Address will be passed unmodified down to the underlying transport.

func (*Server) OnConnection

func (serv *Server) OnConnection(handler ConnectionHandler)

Use the ConnectionHandler to wire up a Request and / or Event handler, and do any other housekeeping required before requests and events are accepted.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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