Seneca wrapper for ES6 and promise application
Create and maintain by Nodeframe Solution
Contact us at support@nf-solution.com
NOTE: This lib had been migrated from nfs-seneca which is officially depreciated. The development and maintenance will continue solely on this repo.
npm install @nodeframe/seneca --save
import
import nfsSeneca from '@nodeframe/seneca';
declare the seneca using this syntax
const {add, act, si} = nfsSeneca(option, transportConfig)
The last value si
is the seneca object itself returned from the seneca initialization.
the implementation of si
object is like so
const si = seneca(option)
and it simply return out for you to use, you can still use si.use()
, si.client()
, or si.listen()
as same as you would use with the official seneca object
The add
and act
is simply the wrapped version of si.add
and si.act
You can declare a service with function,promise and async function
import nfsSeneca from '@nodeframe/seneca';
const {add,act,si} = nfsSeneca(config.seneca.options, config.seneca.transport);
add({...}, (args,done) => {
done(null,{role:'With Done'});
});
add({...}, (args) => {
return Promise.resolve('resolved');
});
add({...}, (args) => {
return {role:'With Return'};
});
add({...}, async (args){
return await p1();
});
There's two type of config that should be pass upon initialization
The first part is the seneca option passed to the seneca initialization. This config will be passed directly to the official seneca initialization. The transportConfig
is where all the magic happen.
the transport config can be like this
transports: {
listenings: [
{
type: 'http',
pins: [
{ role: 'portals', cmd: '*' }
],
port: '8000',
timeout: 30000
}
],
clients: [
{
type: 'http',
pins: [
{ role: 'serviceX', cmd: '*'}
],
port: '8000',
host: process.env.APPLICATION_SERVICE || 'application_service',
timeout: 30000
},
{
type: 'http',
pins: [
{ role: 'serviceY', cmd: '*' }
],
port: '8000',
host: process.env.USER_SERVICE || 'user_service',
timeout: 30000
}
]
}
note that this config is compatible with seneca plugins as well, for example you can use seneca-amqp-transport
plugin (note that this plugin must be npm install
on your main project in order to get this to work)
The example of this amqp
transport config can be like so
transports: {
listenings: [],
clients: [
{
type: 'amqp',
timeout: 5000,
pins: [
{ role: 'USER', cmd: '*' },
{ role: 'permalinkSubmission', cmd: '*' }
],
url: process.env.AMQP_URL || 'amqp://guest:guest@128.199.105.153:5672'
}
]
uses: ['seneca-amqp-transport']
}
The seneca.use('seneca-amqp-transport')
will be run upon initialization by this library.
The other seneca plugin that require more initialization step shall be declared in this package, currently we have several plugin that capable for initialization simply by pushing config file to this setup (of course, you still need to npm install
the plugin into the main project as well)
This plugin will use kubernetes mesh
for seneca
These npm seneca plugins are required for this plugin to work
"seneca-balance-client":
"seneca-consul-registry"
"seneca-mesh"
The mesh initialization on seneca must be run in this manner
seneca
.use('consul-registry',{
host: 'consul-host'
})
.use('mesh',{
pins:[ //listening role
'role:a',
'role:b',
'role:c',
],
host:'host-name-for-this-service',
bases:[
'base-service-host:39999'
],
discover:{
registry:{
active: true
},
multicast:{
active: false
}
} //other option here
})
.ready(function(){
//ready function
})
To avoid duplicate code on your seneca initialization, these setting shall settle in your config only
const config = {
transports: {
type: 'mesh',
listenings: [
{
pins: [
{ role: 'a', cmd: '*' },
{ role: 'b', cmd: '*' },
{ role: 'c', cmd: '*' }
]
}
],
clients: [],
consul: {
host: 'consul-host'
},
mesh: {
host: 'host-name-for-this-service',
bases: ['base-service-host:39999']
},
option: {
//option in this will be merged
discover:{
registry:{
active: true
},
multicast:{
active: false
}
}
}
}
}
notice the mesh.host
is the mandatory field that should specify about this service host name so that other service can connect to this service. The bases array is the base center service according to mesh architecture.
It is important for seneca service to check if the other service to call is available or not. On the contrary, each service should provide a simple health checking for other servie to check if the communication can be made. This will help savign quite some time during debugging process.
@nodeframe/seneca
try to add a mean to handle this problem. When you pass the config into @nodeframe/seneca
, it will automatically read which listening pins this current service provides. It then create another special add
for each pins.
For example, passing this config to @nodeframe/seneca
const seneca = nfsSeneca({}, {
listenings: [
{
type: 'http',
pins: [
{ role: 'a', cmd: '*' },
{ role: 'b', cmd: '*' },
{ role: 'c', cmd: 'cee' }
]
}
],
clients: [
{
type: 'http',
pins: [{role: 'd', cmd: '*'}]
}
]
})
passing this, the health check function will be registered to these pins
{ role: 'a', cmd: '_healthCheck' }
{ role: 'b', cmd: '_healthCheck' }
for other service you can ping to check if this service is online by running this
seneca.act({role: 'a', cmd: '_healthCheck'}, function(err, response) {
console.log(response.result)
//will return {timestamp: <<the current time>>, service: 'a'}
})
Note that role: c
will not register any healthCheck because it could not be sure that all the pins will come into this service, hence there could be clashes on _healthCheck
method. Moreover, note that the role: d
will not be registered also sine it is a service that this current service will consume (not the provider).
To add manual health check to this service, you can do it by sending healthCheck
config like so
const seneca = nfsSeneca({}, {
healthCheck: ['a', 'b']
})
doing this will add cmd: _healthCheck
to role a
and b
Lastly, if you are not interested in the health check, you can disable it by passing
const seneca = nfsSeneca({}, {
disableHealthCheck: true
})
if you want to see the log pulse of _healthCheck
, you should pass
const seneca = nfsSeneca({}, {
logHealthCheckPulse: "true"
})
with this, your application will log something like this every health check pulse
run healthcheck resursively to: payment,MAIL,property,USER,TASK,portals
USER is available at 2017-12-11T21:37:06.691Z
TASK is available at 2017-12-11T21:37:06.691Z
MAIL is available at 2017-12-11T21:37:06.691Z
payment is available at 2017-12-11T21:37:06.692Z
property is available at 2017-12-11T21:37:06.692Z
portals is available at 2017-12-11T21:37:06.695Z
to help you debug the service, we develop a ping function that can pass the same config and ping to the client service
var ping = require('@nodeframe/seneca/libs/tool').getPing(senecaOption, transportConfig)
then, this function can be used to call to health check on the specify service
ping('a').then(function(resp) {
console.log("resp=", resp)
})
will call to service a