Skip to content

RPC library based on net sockets, can work both with tcp sockets and ipc.

License

Notifications You must be signed in to change notification settings

delvedor/rinvoke

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

73 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

rinvoke

js-standard-style Build Status Coverage Status

Build your distributed functions system with Rinvoke!
Rinvoke is a RPC library based on net sockets, can work both with tcp sockets and ipc.
It has built in reconnect logic and supports multiple parser/serializers, such as msgpack or protbuf.
Internally uses tentacoli to multiplex the requests and avvio to guarantee the asynchronous bootstrap of the application, it also provide an handy request validation out of the box with JSON schema.

Install

npm i rinvoke --save

Usage

Rinvoke could be used as a server or client, so when your require it you must specify it.
Let's see an example for the server:

const rinvoke = require('rinvoke/server')()

rinvoke.register('concat', (req, reply) => {
  reply(null, req.a + req.b)
})

rinvoke.listen(3000, err => {
  if (err) throw err
})

And now for the client:

const rinvoke = require('rinvoke/client')({
  port: 3000
})

rinvoke.invoke({
  procedure: 'concat',
  a: 'hello ',
  b: 'world'
}, (err, result) => {
  if (err) {
    console.log(err)
    return
  }
  console.log(result)
})

The client could seem synchronous but internally everything is handled asynchronously with events.
Checkout the examples folder if you want to see more examples!

API

Server

server([opts])

Instance a new server, the options object can accept a custom parser/serializer via the codec field.

const rinvoke = require('rinvoke/server')({
  codec: {
    encode: JSON.stringify,
    decode: JSON.parse
  }
})

The default codec is JSON.
Events:

  • 'connection'
  • 'error'
  • 'request'
  • 'listening'

register(procedureName, [schema,] procedureFunction)

Registers a new procedure, the name of the procedure must be a string, the function has the following signature: (request, reply) where request is the request object and reply a function t send the response back to the client.

rinvoke.register('concat', (req, reply) => {
  reply(null, req.a + req.b)
})

Promises and async/await are supported as well!

rinvoke.register('concat', async req => {
  return req.a + req.b
})

Validation

Rinvoke offers you out of the box a nice and standard way to validate your requests, JSON schema!
Internally uses ajv to achieve the maximum speed and correctness.

rinvoke.register('concat', {
  type: 'object',
  properties: {
    a: { type: 'string' },
    b: { type: 'string' }
  },
  required: ['a', 'b']
}, (req, reply) => {
  reply(null, req.a + req.b)
})

listen(portOrPath, [address], callback)

Run the server over the specified port (and address, default to 127.0.0.1), if you specify a path (as a string) it will use the system socket to perform ipc.

rinvoke.listen(3000, err => {
  if (err) throw err
})

rinvoke.listen(3000, '127.0.0.1', err => {
  if (err) throw err
})

rinvoke.listen('/tmp/socket.sock', err => {
  if (err) throw err
})

Client

client(options)

Instance a new client, the options object must contain a port or path field, furthermore can accept a custom parser/serializer via the codec field. If you want to activate the automatic reconnection handling pass reconnect: true (3 attempts with 1s timeout), if you want to configure the timeout handling pass an object like the following:

const rinvoke = require('rinvoke/client')({
  port: 3000,
  address: '127.0.0.1'
  reconnect: {
    attempts: 5,
    timeout: 2000
  },
  codec: {
    encode: JSON.stringify,
    decode: JSON.parse
  }
})

The default codec is JSON.
Events:

  • 'connect'
  • 'error'
  • 'close'
  • 'timeout'

invoke(request, callback)

Invoke a procedure on the server, the request object must contain the key procedure with the name of the function to call.
The callback is a function with the following signature: (error, response).

rinvoke.invoke({
  procedure: 'concat',
  a: 'hello ',
  b: 'world'
}, (err, result) => {
  if (err) {
    console.log(err)
    return
  }
  console.log(result)
})

Promises are supported as well!

rinvoke
  .invoke({
    procedure: 'concat',
    a: 'a',
    b: 'b'
  })
  .then(console.log)
  .catch(console.log)

fire(request [, callback])

Fire (and forget) a procedure on the server, the request object must contain the key procedure with the name of the function to call.
The optional callback will be called if there is an error while sending the message, or after the message has been sent successfully.

rinvoke.fire({
  procedure: 'concat',
  a: 'hello ',
  b: 'world'
})

timeout(time)

Sets the timeout of the socket.

keepAlive(bool)

Sets the keep-alive property.

Method for both client and server

use(callback)

The callback is a function witb the following signature: instance, options, next.
Where instance is the client instance, options, is an options object and next a function you must call when your code is ready.
This api is useful if you need to load an utility, a database connection for example. use will guarantee the load order an that your client/server will boot up once every use has completed.

rinvoke.use((instance, opts, next) => {
  dbClient.connect(opts.url, (err, conn) => {
    instance.db = conn // now you can access in your function the database connection with `this.db`
    next()
  })
})

onClose(callback)

Hook that will be called once you fire the close callback.

rinvoke.onClose((instance, done) => {
  // do something
  done()
})

close(callback)

Once you call this function the socket server and client will close and all the registered functions with onClose will be called.

rinvoke.close((err, instance, done) => {
  // do something
  done()
})

CLI

You can even run the server with the integrated cli! In your package.json add:

{
  "scripts": {
    "start": "rinvoke server.js"
  }
}

And then create your server file:

module.exports = async req => `Hello ${req.name}!`

You can also use an extended version of the above example:

function sayHello (rinvoke, opts, next) {
  rinvoke.register('hello', (req, reply) => {
    reply(null, { hello: 'world' })
  })

  next()
}

module.exports = sayHello

The options of the cli are:

--port       -p      # default 3000
--address    -a      # default 127.0.0.1
--path       -P      # path of the ipc web socket
--name       -n      # name of your exported function

Acknowledgements

This project is kindly sponsored by LetzDoIt.

License

MIT

Copyright © 2017 Tomas Della Vedova

About

RPC library based on net sockets, can work both with tcp sockets and ipc.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published