feathers icon indicating copy to clipboard operation
feathers copied to clipboard

Breaking a Feathers application into Microservices

Open eyeke04 opened this issue 7 years ago • 18 comments

There isn't still a proper documentation or tutorial on how to break feathers services into microservices, and there has been many request for this. My team is working with feathers and it is really difficult for us to work with all the services in one application. We will like to break them up. Or do we just split the services into multiple feathers apps?

Please how do we do this?

eyeke04 avatar Mar 23 '17 07:03 eyeke04

Yes even i need to understand how can i split into multiple microservices.

kethan avatar Mar 23 '17 07:03 kethan

All Feathers apps are the same, whether built as individual microservices or as a bunch of services on a single machine. Each service is built on top of the application base. @eyeke04 you pretty much hit the nail on the head with your question. You split up your current "monolithic" Feathers app into two or more apps, each with its own service. You can use the Feathers client on the server to create the connection to your other services. Any services that your microservice references will have to be setup as Feathers client services that point to the remote destination. You can treat the Feathers Client as though it were a database adapter.

app.use('/local/service/alias', feathersClient.service('/remote/service'))

marshallswain avatar Mar 23 '17 14:03 marshallswain

When i am posting a data to http://localhost:3000/service1 it is not reflecting but when i directly add to http://localhost:3001/service1 it is showing the data. Why?

//server.js

const feathers = require('feathers');
const bodyParser = require('body-parser');
const client = require('feathers/client');
const rest = require('feathers-rest');
const socketio = require('feathers-socketio');
const socketClient = require('feathers-socketio/client');
const io = require('socket.io-client');

const socket1 = io('http://localhost:3001');
const socket2 = io('http://localhost:3002');

const service1App = client().configure(socketClient(socket1));
const service2App = client().configure(socketClient(socket2));

const app = feathers()
    // Enable REST services
    .configure(rest())
    // Enable REST services
    .configure(socketio())
    // Turn on JSON parser for REST services
    .use(bodyParser.json())
    // Turn on URL-encoded parser for REST services
    .use(bodyParser.urlencoded({ extended: true }))
    .use('/service1', service1App.service('service1'))
    .use('/service2', service2App.service('service2'));


const service1 = service1App.service('service1')
service1.on('created', message => console.log('service1 Created a message', message));

const service2 = service2App.service('service2')
service2.on('created', message => console.log('service2 Created a message', message));

// Start the server.
const port = 3000;

app.listen(port, function () {
    console.log(`Feathers server listening on port ${port}`);
});

///////////////////////// Service1 //////////////////// service1.js ///////////////////////// Service1 ////////////////////

const feathers = require('feathers');
const bodyParser = require('body-parser');
const rest = require('feathers-rest');
const socketio = require('feathers-socketio');
const memory = require('feathers-memory');

// Create a feathers instance.
const app = feathers()
    // Enable REST services
    .configure(rest())
    // Enable REST services
    .configure(socketio())
    // Turn on JSON parser for REST services
    .use(bodyParser.json())
    // Turn on URL-encoded parser for REST services
    .use(bodyParser.urlencoded({ extended: true }));

// Create an in-memory Feathers service with a default page size of 2 items
// and a maximum size of 4
app.use('/service1', memory({
    paginate: {
        default: 2,
        max: 4
    }
}));

// Create a dummy Message
app.service('service1').create({
    text: 'Server message',
    complete: false
}).then(function (message) {
    console.log('Created message', message);
});
const service = app.service('service1');
service.on('created', message => console.log('Created a message', message));
// Start the server.
const port = 3001;

app.listen(port, function () {
    console.log(`Feathers service1 listening on port ${port}`);
});

///////////////////////// Service2 //////////////////// service2.js ///////////////////////// Service2 ////////////////////

const feathers = require('feathers');
const bodyParser = require('body-parser');
const rest = require('feathers-rest');
const socketio = require('feathers-socketio');
const memory = require('feathers-memory');

// Create a feathers instance.
const app = feathers()
    // Enable REST services
    .configure(rest())
    // Enable REST services
    .configure(socketio())
    // Turn on JSON parser for REST services
    .use(bodyParser.json())
    // Turn on URL-encoded parser for REST services
    .use(bodyParser.urlencoded({ extended: true }));

// Create an in-memory Feathers service with a default page size of 2 items
// and a maximum size of 4
app.use('/service2', memory({
    paginate: {
        default: 2,
        max: 4
    }
}));

// Create a dummy Message
app.service('service2').create({
    text: 'Server message',
    complete: false
}).then(function (message) {
    console.log('Created message', message);
});

const service = app.service('service2');
service.on('created', message => console.log('Created a message', message));

// Start the server.
const port = 3002;

app.listen(port, function () {
    console.log(`Feathers service2 listening on port ${port}`);
});
///////////////////////// Service2 ////////////////////

kethan avatar Mar 29 '17 06:03 kethan

@kethan Try changing this line .use('/service1', service1App.service('service1')) to something that you can better debug, like this:

.use('/service1', {
  create (data) {
    debugger;
    return service1App.service('service1').create(data)
      .then(response => {
        console.log(response);
        debugger;
      })
      .catch(error => {
        console.log(error);
        debugger;
      })
  }
})

marshallswain avatar Mar 29 '17 06:03 marshallswain

Odd. I used a similar setup before, it looks like it should work. I'll see if I can reproduce the same problem.

daffl avatar Mar 29 '17 06:03 daffl

@marshallswain I am getting this when i try to post to http://localhost:3000/service1

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>Error</title>
    </head>
    <body>
        <pre>Cannot POST /service1</pre>
    </body>

kethan avatar Mar 29 '17 06:03 kethan

@daffl I am afraid of this setup .. it this optimised way to do microservices or is there any better way you can suggest? How will socket connections scale? I will have each microservices running in cluster.

kethan avatar Mar 29 '17 06:03 kethan

@kethan, what scares you about this setup?

marshallswain avatar Mar 29 '17 12:03 marshallswain

@marshallswain i mean will socket.io service scale?

kethan avatar Mar 29 '17 12:03 kethan

WebSockets are more efficient than regular HTTP 1 requests. Once connected, their overhead is much lower, per request. You're definitely going to need some sort of messaging bus, like feathers-sync. And you should be able to stick a load balancer in front of your services. I recommend at some point swapping the feathers-socketio transport for the feathers-rest one to compare and benchmark performance.

marshallswain avatar Mar 29 '17 13:03 marshallswain

@marshallswain wow that sounds awesome!. How do I use feathers-sync in above example?..

And what about the status of the issue i posted?

kethan avatar Mar 29 '17 13:03 kethan

You should be able to follow the docs in the README.md to get it setup. You'll need more machines, though. ;)

@daffl said he's going to test out the setup, above, so I'm going to defer to that so I can focus on some other work I'm doing.

marshallswain avatar Mar 29 '17 13:03 marshallswain

Hi,

any updates on the issue I posted?

kethan avatar Apr 07 '17 04:04 kethan

Not yet. I'm focusing on getting auth docs squared away, currently. I'll circle back to this afterwards. I have about 4 more guides to write. ;)

marshallswain avatar Apr 07 '17 04:04 marshallswain

Also if you use the socketio solution how do you authenticated between the two. Any endpoints that require authentication will fail. Ideally need a way to allow requests from the microservice through.

dottodot avatar Sep 18 '17 16:09 dottodot

I add my 2 cents on this with https://github.com/kalisio/feathers-distributed, it aims at deploying N feathers apps holding different services talking together, so that you can develop each one independently. It is different from https://github.com/feathersjs/feathers-sync which aims at deploying N feathers apps holding the same services as far as I understand. All of this raises a set of questions like:

  • authentication management
  • API gateway and load balancing
  • remote hooks invokation
  • ...

claustres avatar Feb 06 '18 15:02 claustres

Hey @claustres do you want to add this to https://github.com/feathersjs/awesome-feathersjs? I'm planning on updating feathers-sync and publish an article where I'll mention this one as well.

daffl avatar Feb 06 '18 16:02 daffl

Done through https://github.com/feathersjs/awesome-feathersjs/pull/4. Depending on your schedule I might help on the article because I was thinking of writing something on this as well, I still miss some time to test a production use case...

claustres avatar Feb 06 '18 20:02 claustres