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.
npm i rinvoke --save
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!
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'
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
})
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)
})
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
})
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 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 (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'
})
Sets the timeout of the socket.
Sets the keep-alive
property.
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()
})
})
Hook that will be called once you fire the close
callback.
rinvoke.onClose((instance, done) => {
// do something
done()
})
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()
})
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
This project is kindly sponsored by LetzDoIt.
Copyright © 2017 Tomas Della Vedova