SocketCluster / socketcluster

Highly scalable realtime pub/sub and RPC framework
https://socketcluster.io
MIT License
6.15k stars 314 forks source link

ES6 compatibility #142

Closed JimLiu closed 8 years ago

JimLiu commented 8 years ago

I'd like to write the code with es6, but socketcluster dose not support es6 code.

var SocketCluster = require('socketcluster').SocketCluster;
var socketCluster = new SocketCluster({
  workers: 1, // Number of worker processes
  brokers: 1, // Number of broker processes
  port: 8000, // The port number on which your server should listen
  appName: 'myapp', // A unique name for your app

  /* A JS file which you can use to configure each of your
   * workers/servers - This is where most of your backend code should go
   */
  workerController: __dirname + '/worker.js',

  /* JS file which you can use to configure each of your
   * brokers - Useful for scaling horizontally across multiple machines (optional)
   */
  brokerController: __dirname + '/broker.js',

  // Whether or not to reboot the worker in case it crashes (defaults to true)
  rebootWorkerOnCrash: true
});

The option workerController or brokerController is JS file path, could it changed to Class? for example:

{
    workerController: require('./worker.js')
}

Then I can use babel-register to work with es6, like:

entry.js

require('babel-register');
require('./server');

server.js

import { SocketCluster } from 'socketcluster'
import minimist from 'minimist'
import Worker from './worker'

var argv = minimist(process.argv.slice(2));

var socketCluster = new SocketCluster({
  workers: Number(argv.w) || 1,
  brokers: Number(argv.b) || 1,
  port: Number(argv.p) || 8000,
  appName: argv.n || null,
  workerController: Worker
});
IngwiePhoenix commented 8 years ago

You can use SC with ES6, no problem.

// Import:
import SocketCluster from "socketcluster";

var sc = new SocketCluster({
    brokerController: require.resolve("./mybroker.js")
});

// In mybroker.js:
export var run = function(worker) {
    // ...
}

In theory, this should work.

IngwiePhoenix commented 8 years ago

Also, to load stuff like registrars, use the initController option.

JimLiu commented 8 years ago

Cool, thanks!

JimLiu commented 8 years ago

Unfortunately, it can't work :disappointed:

jondubois commented 8 years ago

Which version of Node.js are you using? New versions should be ES6.

JimLiu commented 8 years ago

@jondubois I use node v4.2 + Babel 6, the socketcluster version is 3.x, base on the sample code: https://github.com/SocketCluster/socketcluster/tree/master/sample

here is the code I changed:

index.js

require('babel-register');
require('./server');

server.js

import { SocketCluster } from 'socketcluster'
import minimist from 'minimist'

var argv = minimist(process.argv.slice(2));

var socketCluster = new SocketCluster({
  workers: Number(argv.w) || 1,
  brokers: Number(argv.b) || 1,
  port: Number(argv.p) || 8000,
  appName: argv.n || null,
  workerController: __dirname + '/worker.js',
  brokerController: __dirname + '/broker.js',
  socketChannelLimit: 1000,
  rebootWorkerOnCrash: argv['auto-reboot'] != false
});

run with

node index.js

works perfect if I did not change anything with worker.js or broker.js

but if I tried to write some ES6 code like this:

broker.js

module.exports.run = function (broker) {
    const { pid } = process; // es6 code
    console.log('   >> Broker PID:', pid);
};

Then got an error:

$ node index
   [Busy] Launching SocketCluster
/sttest/broker.js:2
    const { pid } = process; // es6 code
          ^

SyntaxError: Unexpected token {
    at exports.runInThisContext (vm.js:53:16)
    at Module._compile (module.js:414:25)
    at Object.Module._extensions..js (module.js:442:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:311:12)
    at Module.require (module.js:366:17)
    at require (module.js:385:17)
    at Object.<anonymous> (/sttest/node_modules/socketcluster/node_modules/iocluster/node_modules/ndata/server.js:25:23)
    at Module._compile (module.js:435:26)
    at Object.Module._extensions..js (module.js:442:10)
1450425115495 - Origin: IOCluster
   [Error] Error: nData server at socket path /var/folders/4c/hh3fk19n3sq_djpcft9vpf7m0000gn/T/socketcluster/71bea5cc-fa0b-4348-b8f4-b7027900b448_9cba8c95ae/b0 exited
    at EventEmitter.<anonymous> (/sttest/node_modules/socketcluster/node_modules/iocluster/index.js:353:28)
    at emitTwo (events.js:87:13)
    at EventEmitter.emit (events.js:172:7)
    at ChildProcess.<anonymous> (/sttest/node_modules/socketcluster/node_modules/iocluster/node_modules/ndata/index.js:55:10)
    at emitTwo (events.js:87:13)
    at ChildProcess.emit (events.js:172:7)
    at Process.ChildProcess._handle.onexit (internal/child_process.js:200:12)
/sttest/broker.js:2
    const { pid } = process; // es6 code
JimLiu commented 8 years ago

I'm not sure if I use it right, or any ES6 sample code I can refer to?

IngwiePhoenix commented 8 years ago

Mine. I'll upload it for you real quick (: I need to commit my changes, anyway... So give me a sec and youll have an example! ^^

IngwiePhoenix commented 8 years ago

Ta-da. https://github.com/DragonsInn/BIRD3/blob/master/app/Backend/Service/SocketCluster.js

JimLiu commented 8 years ago

I tried, but did not get the point. server.js works, but worker.js can't work with ES6 Is there any ES6 sample base on this? https://github.com/SocketCluster/socketcluster/tree/master/sample

IngwiePhoenix commented 8 years ago

Are you aware, that worker.js is a new process? In order to make it ES6 aware, use initController.

Instantiation: https://github.com/DragonsInn/BIRD3/blob/master/app/Backend/Service/SocketCluster.js#L18-L20 The individual files: https://github.com/DragonsInn/BIRD3/tree/master/app/Backend/SocketCluster

I highlighted the relevant lines for you. Please see the mentioned files, to understand the flow.

JimLiu commented 8 years ago

Yes, I missed the initController config, will try it later :)

IngwiePhoenix commented 8 years ago

I was the one to implement that feature for SC, for the sole reason of being able to drop registrars in, to allow custom JS dialects and transpilers to work. :).

JimLiu commented 8 years ago

Cool, as I suggested: The option workerController or brokerController is JS file path, could it changed to Class? Then we do not need use initController

IngwiePhoenix commented 8 years ago

Thats not possible. The files are being used to render a new process, so you can not drop arbitary objects there, instead you'll need to supply the path to a module that can be run instead.

JimLiu commented 8 years ago

I don't think it should be a path if it's used to render a new process.

I checked the code: https://github.com/SocketCluster/socketcluster/blob/master/lib/scworker.js#L240-L241

  this._workerController = require(this._paths.appWorkerControllerPath);
  this._workerController.run(this);

it require the path as an instance, then execute it's run method.

so the workerController option could be a class too. for example:

export default class Worker {
  run() {...}
}
import Worker from './worker'
var socketCluster = new SocketCluster({
  workerController:  Worker,
});

in scworker.js

var WorderController = this.option.workerController || defaultWorderController;
var worker = new WorderController();
workder.run();
IngwiePhoenix commented 8 years ago

Your research wasn't deep enough:

https://github.com/SocketCluster/socketcluster/issues/134#issuecomment-156849430

I had done this research on my own for a system I am coding, its also part of BIRD3. Read the linked post, it should be able to give you some insights of the forking and clustering done in SC. This will also explain why you can't just throw objects around - i.e., classes.

JimLiu commented 8 years ago

@IngwiePhoenix looks like you are right!

And it works after add initController

var socketCluster = new SocketCluster({
  initController:  __dirname + '/init.js',
});

init.js

module.exports.run = function(thisWorker) {
    require("babel-register");
}

Thanks :)

IngwiePhoenix commented 8 years ago

Glad you got it working! :) Have fun with ES6!

vsnig commented 8 years ago

Is it possible to use ES6 inside the Broker? Seems like initController option has no effect on the Broker.

IngwiePhoenix commented 8 years ago

Of course it does. initController was designed for that very purpose, to allow you to modify the global execution context. Are you using babel-register correctly? You need to explicitly call the function it returns - a mistake some people tend to do.

vsnig commented 8 years ago

Yes, I call it like this require("babel/register")({stage: 0}); It does work in Workers but doesn't work in Broker somehow..

IngwiePhoenix commented 8 years ago

Odd. It should. I mean, I do the very same thing and it works. Hm…

You can see my working stuff here: https://github.com/dragonsinn/bird3/tree/master/app/Backend/SocketCluster https://github.com/dragonsinn/bird3/tree/master/app/Backend/SocketCluster

vsnig commented 8 years ago

May I ask you to drop a line of ES6 (like import fs from 'fs') to the top of your Broker.js and try to run it? (At the moment you don't have ES6 code there). I've just tried to test it on fresh SocketCluster boilerplate project and it is the same - ES6 works in Worker, but in Broker it doesn't. It looks like a bug.

IngwiePhoenix commented 8 years ago

Actually youre right. I re-wrote my broker. Im going to send a proper PR to fix that. Since i am the one that made initController, i should also fix it ^^

vsnig commented 8 years ago

Found it. The problem is that the code of the Broker is being required before initController's run method is called https://github.com/SocketCluster/ndata/blob/master/server.js#L25

IngwiePhoenix commented 8 years ago

Yup. just wanted to post the same. Im going to hotfix it.

IngwiePhoenix commented 8 years ago

Well I just tried something else, and that actually avoids fixing!

// Enable OJ support inside NodeJS
require("oj-node");

// Bring in babel
require("../../bootstrap/nodejs/autoload");

// Enable Uniter support
//require("uniter-node");

module.exports.run = function(thisWorker) {

    // Time to bootstrap workers.

    // Put a global BIRD3 object in place
    global.BIRD3 = require("../../Support/GlobalConfig");
    //if(thisWorker.kind == "worker") { require("../Communicator")(redis, sc); }

}

(ignore my comments...)

You can also just supply an empty run method. But for reigstrars, just add them outside of it. I will think of a better way by fixing this code, so it is symmetric to SCWorkerCluster...but for now that will do.

IngwiePhoenix commented 8 years ago

https://github.com/SocketCluster/ndata/pull/19

vsnig commented 8 years ago

Thanks :thumbsup:

Kannaj commented 8 years ago

Hello - is it ok to to use this method for production? According to babel , the module isnt meant for production

babel/example-node-server#13

Is there any other way possible though?