@superdevofficial/feathers-distributed v0.7.2
feathers-distributed
Distribute your Feathers services as microservices
The master
branch and >= 0.3.x version is expected to work with Feathers v3 (a.k.a. Buzzard).
The auk
branch and 0.2.x version is expected to work with Feathers v2 (a.k.a. Auk).
This plugin relies on cote and takes benefits of it:
- Zero-configuration: no IP addresses, no ports, no routing to configure
- Decentralized: No fixed parts, no "manager" nodes, no single point of failure
- Auto-discovery: Services discover each other without a central bookkeeper
- Fault-tolerant: Don't lose any requests when a service is down
- Scalable: Horizontally scale to any number of machines
- Performant: Process thousands of messages per second
cote requires your cloud provider to support IP broadcast or multicast. You can still have the same functionality with Weave overlay networks, eg on Docker's Cloud. In any other cases you can use centralized discovery.
cote works out of the box with Docker Swarm and Docker Cloud but we are seeking for volunteers to test this module under various Cloud providers like AWS, Google Cloud, etc. Please open an issue if you'd like to do so and report your findings.
You might find this presentation really helpful to understand it.
You might also be interested in reading this typical use case.
Installation
npm install @kalisio/feathers-distributed --save
To get the latest version please use the following command:
npm install https://github.com/kalisio/feathers-distributed --save
feathers-distributed
is as least intrusive as possible so for most use cases you simply need to configure it along with your applications holding your services:
const distribution = require('@kalisio/feathers-distributed');
...
app.configure(hooks());
app.configure(socketio());
app.configure(distribution());
...
Documentation
When the plugin initializes the following is done for your app:
- creates a publisher to dispatch its locally registered services to other nodes.
- creates a subscriber to be aware of remotely registered services from other nodes.
What is done by overriding app.use
is the following:
- each local Feathers service of your app creates a responder to handle incoming requests from other nodes.
- each local Feathers service of your app creates a publisher to dispatch service-level events to other nodes.
What is done when your app is aware of a new remotely registered service is the following:
- creates a local Feathers service acting as a proxy to the remote one by creating a requester to send incoming requests to other nodes.
- this proxy service also creates a subscriber to be aware of service-level events coming from other nodes.
Configuration options
Local services
By default all your services will be exposed, you can use the services
option to indicate which services need to be published if you'd like to keep some available only internally:
app.configure(
distribution({
// Can be a static list of service path to be exposed
services: ['api/service1', 'api/service2']
// Can be a function returning true for exposed services
services: (service) => (service.path !== 'api/internal')
})
)
Remote services
By default all remote services will be consumed, you can use the remoteServices
option to indicate which services need to be consumed if you don't want to be polluted by unused ones:
app.configure(
distribution({
// Can be a static list of service path to be consumed
remoteServices: ['api/service1', 'api/service2']
// Can be a function returning true for consumed services
remoteServices: (service) => (service.path !== 'api/external')
})
)
You can add hooks to each registered remote service by using the hooks
option, this is typically useful to enforce authentication on a gateway scenario:
app.configure(
distribution({
hooks: {
before: {
all: [authenticate('jwt')]
},
},
})
);
You can add middlewares to each registered remote service by using the middlewares
option, this is typically useful to enfore correct error handling on a gateway scenario:
const express = require('@feathersjs/express')
app.configure(
distribution({
middlewares: {
before: (req, res, next) => next(),
after: express.errorHandler()
},
})
);
Indeed, Feathers does not allow to register new services after the app has been setup so that application middlewares like not found or error handler will be hit first but feathers-distributed
dynamically adds new services during app lifecycle. You thus need to register middlewares whenever a new service pops up.
Events
By default all real-time events from local services are distributed to remote ones but you can customize the events to be dispatched by providing the list in the distributedEvents
property of your service or disable all events publishing with the publishEvents
boolean option.
Hooks
In some cases it can be useful to know in a hook if the method has been called from a remote service or a local one (e.g. in order to skip authentication). For this you can use the fromRemote
flag in parameters:
services[i].hooks({
before: {
all: hook => {
// Do something specific in this case
if (hook.params.fromRemote) ...
return hook
}
}
})
Example
To launch the example:
npm start
Wait a couple of seconds so that each app is aware of other apps on the network. Open the example/index.html file in your browser, you should see a TODO coming from a microservice.
Look for details into the example folder.
Authentication
There are two scenarios:
- the API gateway, where you have a single entry point (ie node) to authenticate and access your API but services are distributed accross different nodes
- the distributed application, where you can distribute and access any service on any node on your network mesh with authentication
API gateway:
In this case you have to install the authentication plugin on your gateway and register a hook that will enforce authentication on each registered remote service by using the hooks
option:
app.configure(
distribution({
hooks: {
before: {
all: [authenticate('jwt')],
},
},
})
);
You don't need to install the authentication plugin or hook on each service served from your nodes.
You process as usual to authenticate your client first on the gateway with a local or JWT strategy for instance.
Our example folder is a good start for this use case.
Distributed application
In this case you have to install the authentication plugin on each of your nodes and register a hook that will enforce authentication on each service as usual.
You process as usual to authenticate your client first on any node with a local or JWT strategy for instance.
Our tests contain a good example for this use case.
To make it work all nodes must share the same authentication configuration (i.e. secret)
Tips
Initialization
1) The library overrides app.use()
to automatically publish any new service defined, so that you can usually safely initialize it before registering your services like others feathers plugins (transport, configuration, etc.). However, you might also configure some middlewares with options.middlewares
and in this case you probably need to initialize the express plugin beforehand.
2) The library immediately initializes the underlying cote module unless you intentionally add some delay (coteDelay
option in ms, defaults to none). This delay can be required because it appears that in some scenarios, e.g. Docker deployment the network setup takes some time and cote is not able to correctly initialize (e.g. allocate ports or reach Redis) before.
3) As the library also relies on cote components to publish/subscribe events, and these components take some time to initialize, there is also a publication delay (publicationDelay
option in ms, defaults to 10s) that is respected before publishing app services once initialized.
Environment variables
Some options can be directly provided as environment variables:
COTE_LOG
to activate logging for all underlying cote componentsBASE_PORT
to select the starting port of the port range to be used by coteHIGHEST_PORT
to select the ending port of the port range to be used by coteCOTE_DELAY
(ms) to define the delay before initializing cotePUBLICATION_DELAY
(ms) to define the delay before publishing services
License
Copyright (c) 2017 Kalisio
Licensed under the MIT license.