GithubHelp home page GithubHelp logo

Comments (3)

jondubois avatar jondubois commented on May 12, 2024

What you pointed out is correct, each worker has its own scope and cannot access each other's variables directly as you suggested.

Also, note that you should treat workers as though they are disposable and can crash at any time - This is a central idea of the 12-factor app (http://12factor.net/disposability).

In SC, you can share events/data between workers using the global nData object (accessible through property of worker object):

worker.global

The global object is an nData client (https://github.com/topcloud/ndata) which you can use to store/retrieve data asynchronously in a central place to share between all workers. It's basically a lightweight version of Redis which runs entirely in Node.js.

Note that in SocketCluster, a client will always be sent to the same worker (this applies to every HTTP request and WebSocket connection made during that session). If a user has multiple tabs open in the browser (multiple sockets), they will all be sent to the same worker as well.

There are a few general rules for using variables inside workers:

  • These variables should only store data which concerns a user/session/socket which is bound to the current worker, and;
  • The data that you store inside these variables shouldn't be operation-critical - Basically, you shouldn't store data in a variable if that data is necessary for your system to continue to operate normally after a worker crash. (You should assume that your workers are disposable)

All this said, when designing a distributed system, you shouldn't try to store individual clients as you are trying to do. This works against the principles of the Publish/Subscribe pattern (http://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern).

You should try to resist the urge of having all your workers 'know' about all of your clients (this approach is simply not scalable). Instead, you should design your system in such a way that you do not have to keep track of them at all (yes it's possible) - You will find this MUCH easier to deal with and it will scale seamlessly.

Your clients should listen for events that they are interested in - In your workers, you can use the SUBSCRIBE middleware to authorize/block them from doing so. Once your client is subscribed to a channel, they will receive all events that you publish to it. You don't have to know which clients are subscribed to that channel in order to send messages to them.

For example, you could have an event channel for a user ('Bob') and another user ('Alice') could send messages to that channel by emitting to it.

For example, assume this client-side code (for Bob):

socket.on('Bob', function (data) {
    console.log('Received message from ' + data.from + ': ' + data.message);
});

Then assume this client-side code (for Alice):

socket.publish('Bob', {
    from: 'Alice',
    message: 'Hi Bob!'
});

Here Alice is sending a message to Bob's event channel. Only clients which are subscribed to the 'Bob' event will receive the message (in this case just Bob, no one else). As mentioned before, you can control who can subscribe to a channel using middleware - You can control access by authenticating the user. See this post for more details: http://ncombo.wordpress.com/2014/08/22/full-stack-pubsub-with-socketcluster/

from socketcluster.

jondubois avatar jondubois commented on May 12, 2024

Your approach to handling events sounds good.

With regard to middleware, you're almost there.
Note that you should always call next() eventually (for every publish message).
If you call next() with no arguments then it means allow, if you call next with a a string as argument like:

next('You are not allowed to publish to this channel...')

then that means fail.
If you never call next() (as would happen in some cases in the example you showed) then it will timeout and throw a timeout error on the client (which doesn't give much information to the browser) - It's better to call next() with an explicit error to let the browser know exactly why they are not allowed to publish to that channel.

Also, I feel that you don't need middleware to do what you're trying to do. If you don't want a specific client socket to receive an event - All you have to do is not listen to that event.
SocketCluster is really efficient at making sure that only client sockets which are listening to an event will receive that event.

You generally don't need to block a user/socket from publishing an event to their own channel since there is no security issue with that. The publish middleware is mostly used for preventing other users from publishing events to a channel which they shouldn't have access to.

Documentation on middleware is lacking at the moment, so I'll do my best to provide an explanation:

Following from the example I gave earlier.
If you have a channel for 'Bob', you might want to setup a SUBSCRIBE middleware function which will make sure that only the sockets which belong to the user Bob will be allowed to listen to that event. On the publish side, you might want to add middleware to make sure that only friends of Bob are allowed to post messages to his channel.

To make good use of middleware, you need some way to associate sockets (or a session) with a particular user's identify. You can do this in a number of ways (you can make use of tools like Redis or other database engines to keep track of session IDs and authentication tokens). Note that you can get the session id from:

socket.session.id

However, probably the easiest way is to use SC's socket.session (also accessible over HTTP using req.session) object directly to set/get auth tokens, you can perform authentication over either HTTP or over a realtime connection (whatever works best for you):

Example (server) code:

scServer.on('connection', function (socket) {
  socket.on('login', function (data) {
    // Get user's actualPassword from a database then compare it with 
    // the provided data.password to check if the user is who they claim to be
    // Here we are using MySQL (see https://github.com/felixge/node-mysql/)
    connection.query('SELECT * from user WHERE username = ?', [data.username], 
      function (err, rows) {
        var actualPassword = rows[0].password;

        if (data.password == actualPassword) {
          // Password matches so set a token to associate this session with the username
          socket.session.set('username', data.username);
        }
      }
    );
  });
});

Then the subscribe middleware could look like this (Note that this is highly simplified):

scServer.addMiddleware(scServer.MIDDLEWARE_SUBSCRIBE, function (socket, event, next) {
  socket.session.get('username', function (err, username) {
    if (event.indexOf(username) == 0) {
      // If the event begins with the session's username token,
      // then allow it to subscribe to that event
      next();
    } else {
      // Otherwise block with appropriate error message
      next("ERROR - You cannot subscribe to other users' channels");
    }
  });
});

from socketcluster.

 avatar commented on May 12, 2024

Thanks for the tip for the next method with parameters.

The middleware example provided can't be used because in my case 1 username can have multiple events.

I try to achieve this: sending to all clients except sender:

// Example from socket.io
 socket.broadcast.emit('message', "this is a test");

from socketcluster.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.