RocketChat:Streamer
2 way communication over DDP with better performance.
Installation
meteor add rocketchat:streamer
Why
DDP subscriptions keeps track of all data each client has, it's called MergeBox, this is a lighter solution.
Use subscriptions to send data to client is not always easy to do, with streamer you can send data as simple as calling events.
Use case
You should use this library when you want to send data from server to client of from client to server and prevent the server to keep track off data all clients have.
You can send a lot of data without performance problems, like notifications.
Downsides
Since the library don't keep track of data, you will not receive lost data while offline after reconnection. But we have an event to notify you on reconnections passing the latest record received so you can call a method to verify and get the missing data.
Compatibility
Since streamer is based on DDP, we use subscriptions and methods to send data, it's 100% compatible with all DDP clients, more details below.
Advantages over Arunoda's stream library
- Faster and use less memmory
- For 2000 events and 10 messages (40 thousand operations - send and reply)
- rocketchat:streamer: ~34 seconds, increase of ~68mb of RAM
- arunoda:meteor-stream: ~41 seconds, increase of ~76mb of RAM
- For 10 events and 2000 messages (40 thousand operations - send and reply)
- rocketchat:streamer: ~32 seconds, increase of ~35mb of RAM
- arunoda:meteor-stream: ~43 seconds, increase of ~80mb of RAM
- For 2000 events and 10 messages (40 thousand operations - send and reply)
- Read permission cached by event name by client at subscription time
- Don't use collection at client side (there is a compatible mode if you need)
- Last message cache and reconnection event
- Keep the last message on client for each event name
- You can get the last message and send to the server to verify if you lost messages
How to use
A simple console chat
1const streamer = new Meteor.Streamer('chat'); 2 3if(Meteor.isClient) { 4 sendMessage = function(message) { 5 streamer.emit('message', message); 6 console.log('me: ' + message); 7 }; 8 9 streamer.on('message', function(message) { 10 console.log('user: ' + message); 11 }); 12} 13 14if (Meteor.isServer) { 15 streamer.allowRead('all'); 16 streamer.allowWrite('all'); 17}
Now you can open 2 browser tabs/windows and chat using sendMessage("text")
at your browser's console
Every message will travel from your client to server and retransmited to all other clients.
new Meteor.Streamer('name')
Client
1new Meteor.Streamer(name, [options])
- name - String REQUIRED Unique name to identify stream between server and client
- options - Object OPTIONAL
- useCollection - Boolean Set to
true
to enable the compatible modedefault false
- ddpConnection - Object The DDP connection to be used
default Meteor.connection
- useCollection - Boolean Set to
Server
1const streamer = new Meteor.Streamer(name, [options]);
- name - String REQUIRED Unique name to identify stream between server and client
- options - Object OPTIONAL
- retransmit - Boolean Set to false to prevent streaming "client to client"
default true
- retransmitToSelf - Boolean Set to true if you want to receive messages you've sent via retransmit
default false
- retransmit - Boolean Set to false to prevent streaming "client to client"
.emit('eventName', ...args)
With emit
you send data from client to server or from server to clients.
1streamer.emit(eventName, arg1, arg2, ...);
- eventName - String REQUIRED The event name to send data
- args - Mixed OPTIONAL You can pass 0 or more arguments to your event
If you emit an event from client you will receive that event on server and if you emit an event on your server you will receive the event in all connected clients.
If retransmit
is enabled, you will receive the event emited on client on your server and on all other connected clients.
The default permission deny all comunication, you should define a new permission! See allowRead and allowWrite bellow
1// Example 2streamer.emit('message', {text: 'My new message', user: 'User1'}); // Send one object 3streamer.emit('message', 'My new message', 'User1'); // Send 2 strings 4streamer.emit('message'); // Just call the event without params
.on('eventName', fn)
With on
you listen for data sent from client to server or from server to clients.
1streamer.on(eventName, fn);
- eventName - String REQUIRED The event name to receive data
- fn - Function REQUIRED Function to receive and process the data for the event
The default permission deny all comunication, you should define a new permission! See allowRead and allowWrite bellow
1// Example 2streamer.on('message', function(message) { 3 console.log(message); 4});
.allowRead('eventName', 'all') (Server only)
This permission will be evaluate only 1 time per client, so you decide if the connections is allowed or not, you can't manage permissions based on data.
1streamer.allowRead([eventName], permission);
- eventName - String OPTIONAL The event name to apply permissions, if not informed will apply the permission for all events
- permission - Function/String REQUIRED
- Function(eventName) The function should return true to allow read
- Param eventName The event name, useful when use one function to manage permissions for all events
- Scope this.userId The id of the logged user
- Scope this.connection The connection between client and server
- String There are shortcuts for permissions
- all Allow read for everyone
- none Deny read for everyone
- logged Allow read for logged users
- Function(eventName) The function should return true to allow read
1//Examples 2 3streamer.allowRead('all'); // Everyone can read all events 4 5streamer.allowRead('chat', 'logged'); // Only logged users can read chat events 6 7streamer.allowRead('notifications', function() { // Only admin users can read notificaiton events 8 if (this.userId) { 9 const user = Meteor.users.findOne(this.userId); 10 if (user && user.admin === true) { 11 return true; 12 } 13 } 14 15 return false; 16});
.allowEmit('eventName', 'all') (Server only)
This permission will be evaluate (executed) for each data for each client subscribed, this is much more expensive than allowRead but you can manage permissions based on each data.
1streamer.allowEmit([eventName], permission);
- eventName - String OPTIONAL The event name to apply permissions, if not informed will apply the permission for all events
- permission - Function/String REQUIRED
- Function(eventName, ...args) The function should return true to allow emite
- Param eventName The event name, useful when use one function to manage permissions for all events
- Scope this.userId The id of the logged user
- Scope this.connection The connection between client and server
- String There are shortcuts for permissions
- all Allow emit for everyone [default]
- none Deny emit for everyone
- logged Allow emit for logged users
- Function(eventName, ...args) The function should return true to allow emite
1//Examples 2 3streamer.allowEmit('all'); // Everyone can emit all events 4 5streamer.allowEmit('chat', 'logged'); // Only emit for logged users
.allowWrite('eventName', 'all') (Server only)
1streamer.allowWrite([eventName], permission);
- eventName - String OPTIONAL The event name to apply permissions, if not informed will apply the permission for all events
- permission - Function/String REQUIRED
- Function(eventName, ...args) The function should return true to allow write
- Param eventName The event name, useful when use one function to manage permissions for all events
- Scope this.userId The id of the logged user
- Scope this.connection The connection between client and server
- String There are shortcuts for permissions
- all Allow write for everyone
- none Deny write for everyone
- logged Allow write for logged users
- Function(eventName, ...args) The function should return true to allow write
1//Examples 2 3streamer.allowWrite('all'); // Everyone can write all events 4 5streamer.allowWrite('chat', 'logged'); // Only logged users can write chat events 6 7streamer.allowWrite('notifications', function(eventName, type) { // Only admin users can write notificaiton events 8 if (this.userId && type === 'new-message') { // and only if the first param is 'new-message' 9 const user = Meteor.users.findOne(this.userId); 10 if (user && user.admin === true) { 11 return true; 12 } 13 } 14 15 return false; 16});