feathersjs / feathers

The API and real-time application framework
https://feathersjs.com
MIT License
14.98k stars 744 forks source link

Breaking a Feathers application into Microservices #536

Open eyeke04 opened 7 years ago

eyeke04 commented 7 years ago

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?

kethan commented 7 years ago

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

marshallswain commented 7 years ago

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'))
kethan commented 7 years ago

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 ////////////////////
marshallswain commented 7 years ago

@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;
      })
  }
})
daffl commented 7 years ago

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

kethan commented 7 years ago

@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 commented 7 years ago

@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.

marshallswain commented 7 years ago

@kethan, what scares you about this setup?

kethan commented 7 years ago

@marshallswain i mean will socket.io service scale?

marshallswain commented 7 years ago

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.

kethan commented 7 years ago

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

And what about the status of the issue i posted?

marshallswain commented 7 years ago

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.

kethan commented 7 years ago

Hi,

any updates on the issue I posted?

marshallswain commented 7 years ago

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. ;)

dottodot commented 6 years ago

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.

claustres commented 6 years ago

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:

daffl commented 6 years ago

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.

claustres commented 6 years ago

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...