SignalR external service timed loop - server or client - signalr

I have external services that I want to call every 20-30 seconds and make accessible to the client using SignalR but I'm not sure if I should have the client call the server in a JavaScript setInterval loop or if I should have the server run its own loop and push the information to the client. I'm looking to do dynamic page updates to show what a user is currently playing on Xbox 360/Xbox One or PlayStation Network.
Currently, I'm calling the server side code from the client in a setInterval loop, but I'm not sure how well this will scale. I had been doing Web API 2 calls through AJAX previously, but that will quickly eat up bandwidth. The external services are not mine so the only way to get simulated real-time results is to send a request to the external services periodically. The only problem I see with doing a server side loop is thread blocking. I don't want to stop the current thread while the loop is running.
Client side code:
var profile = $.connection.profileHub;
profile.client.receiveXboxProfile = function (profile) {
console.log(profile.GamerTag);
};
profile.client.receivePSNProfile = function (profile) {
console.log(profile.PSNId);
};
$.connection.hub.start()
.done(function () {
function GetGamerProfiles() {
profile.server.sendXboxProfile(window.UserName)
.done(function() {
})
.fail(function (error) {
console.log('SignalR ' + error);
});
profile.server.sendPSNProfile(window.UserName)
.done(function () {
})
.fail(function (error) {
console.log('SignalR ' + error);
});
}
GetGamerProfiles();
var id = setInterval(function () {
GetGamerProfiles();
}, 20000);
});
Server side hub:
[Authorize]
public async Task SendXboxProfile(string username)
{
// Makes call to external service to get Xbox profile
Clients.Caller.receiveXboxProfile(await xboxapi.GetProfile(username));
}
[Authorize]
public async Task SendPSNProfile(string username)
{
// Makes call to external service to get PSN profile
Clients.Caller.receivePSNProfile(await psnapi.GetProfile(username));
}
Maybe SignalR isn't the best solution for this problem since I don't control the external services, but I am very intrigued by the concept of SignalR and HTML 5 websockets. In general, is it better to do periodic updates on the client or server with SignalR?

Related

Why client does not instantly return from client method?

client method
Meteor.methods({
insertPost: function(data) {
console.log('client')
Posts.insert(data, function(err, ret) {
console.log('client insert end')
});
},
});
server method
Meteor.methods({
insertPost: function(data) {
console.log('server')
Meteor._sleepForMs(200000);
Posts.insert(data, function(err, ret) {
console.log('server insert end')
});
},
});
client submit
'click #save': function(e) {
// data = ....
Meteor.call('insertPost', data, function(error) {
Router.go('/');
});
},
Why does client stuck at form page, instead of instantly going to '/'.
Here is the meteor documentation about it.
Calling methods on the client defines stub functions associated with
server methods of the same name. You don't have to define a stub for
your method if you don't want to. In that case, method calls are just
like remote procedure calls in other systems, and you'll have to wait
for the results from the server.
If you do define a stub, when a client invokes a server method it will
also run its stub in parallel. On the client, the return value of a
stub is ignored. Stubs are run for their side-effects: they are
intended to simulate the result of what the server's method will do,
but without waiting for the round trip delay. If a stub throws an
exception it will be logged to the console.

Meteorjs: Remove reactivity to a certain publish statement

Is there any way to publish once the subscribe request is made and then stop pushing the changes that are made to the collection until the client subscribes again?
I have this scenario:
Server:
Meteor.publish("posts", function () {
return Messages.find(); //Do not push changes to this collection!
});
Client:
Meteor.subscribe("posts");
If you just have to do a one-shot data send to the client, using a method may work:
//Server
Meteor.methods({
getSomePosts : function(limit)
{
check(limit, Number);
return Posts.find({}, {limit : limit}).fetch();
}
});
//Client
Meteor.call('getAllPosts', function(err, result) {
//Do stuff with the result
});
Note that this will be heavy on your database, you may want to use a variable and update it periodically rather than fetching the whole collection each time a client calls the method.
More about limits in the doc!

Server send command to all clients in Meteor

I have a public client side function:
test = function(){
alert("HELLO")!;
}
What I need is a function that works like this - so on the admin client script, they hit a button which calls a server method:
Meteor.call("callFunctions");
The server side function:
Meteor.methods({
callFunctions: function() {
//call clientside function test() for all clients
}
});
How can I do this without depending on a database?
My client-call package does that. It depends on database internally, but you don't have to worry about that dependence.
To call the methods on all clients you'll need to manually register and loop through their ids.
Set up method:
Meteor.ClientCall.methods({
'alert': function() {
...
},
});
Call it:
Meteor.ClientCall.apply(clientId, 'alert', []);

Invoke a client js function in Meteor after getting results from the server

I'm trying to see how can I invoke a js function after the client gets a result from a Meteor method call. The only thing I was able to get is to invoke the function myFunc only on the client that made the actual method call.
Any thoughts how i can invoke the function on all the currently subscribed clients?
here is the code:
function myFunc(error, result) {
alert(result);
}
if (Meteor.is_client) {
Template.container.events = {
'click input' : function () {
Meteor.call('someMethod',myFunc);
if (typeof console !== 'undefined')
console.log("You pressed the button");
}
};
}
if (Meteor.is_server) {
Meteor.startup(function () {
// code to run on server at startup
});
}
Meteor.methods({
someMethod: function() {
//console.log(!this.is_simulation);
return "something";
}
})
Thanks
Currently you can't broadcast a method call to all clients directly. At least as far as I can tell. But a work around would be to create a collection called Alerts and monitor it for changes. Then when you want to send a message to all your users you can change the document in Alerts:
Client:
Alerts = new Meteor.Collection("alerts")
Meteor.autosubscribe(function() {
Alerts.find().observe({
added: function(item){
alert(item.message);
}
});
});
Server:
Alerts = new Meteor.Collection("alerts")
Meteor.publish("alerts", function(){
Alerts.find();
});
Alerts.remove({}); // remove all
Alerts.insert({message: "Some message to show on every client."});
Another option is using Meteor Stream package which purpose is to avoid using a mongodb collection on the server side. It does supports client to clients, server to clients, client to server AND server to servers messaging, including a support for Meteor Cluster
If you want to stay with meteor only using collections, the following code allows you to either broadcast a message from the client to all the clients or a message from the server to all the subscribed clients. Just use this mechanism to then fire a function on the client side once the right message is received. The code is made in such a way that you will never have useless items remaining into the collection.
Messages = new Meteor.Collection("messages");
if (Meteor.isClient) {
Meteor.subscribe("messages");
var query = Messages.find({});
var handle = query.observe({
added: function(document)
{
console.log(document.message);
}
});
// Test the mechanism from the client side
Meteor.call("client talked");
}
if (Meteor.isServer) {
Meteor.startup(function() {
Messages.remove({});
});
Meteor.publish("messages", function()
{
// you might add an optional filter in order to broadcast only the messages you want to the client
return Messages.find();
});
function talk(message)
{
var id = Messages.insert({"message":message});
Messages.remove(id);
}
Meteor.methods(
{
talk: function(message)
{
// you might filter here if the clients can talk using this.userId
talk(message);
}
});
// test the mechanism from the server side
talk("server talked");
}
I like what Zeke said, but for people who uses Meteor 0.5.0+, use Deps.autorun instead of autosubscribe... details at:
https://groups.google.com/forum/#!topic/meteor-core/mTa81RLvhbY
and
http://www.meteor.com/blog/2013/02/14/meteor-055-devshop-code-and-community-contributions
I simple approach to call a JavaScript client-side function would be to add a script tag in your html template that is bound by your collection. Anytime a new item is inserted, this tag would be inserted into the client would run your function. I have a collection call uploads with some properties such as name. The following template triggers drawpoints() client-side function upon receipt of a new item in Uploads collection:
{{#each uploads}}
<tr>
<td>{{name}}</td>
<td>
<div class="alert alert-success">Download Here</div>
</td>
</tr>
<script>drawpoints();</script>
{{/each}}

Dealing with context of server responses in realtime web applications

Finding it hard to describe this issue - so please edit if you know more relevant terms.
I'm building a web application which essentially uses Redis (PubSub) + Node.js + Socket.IO as a distribution server.
I have two-way communication working with no issues - but I need to be able to make a request to the server from the client (asynchronously) and deal with the response while still processing other irrelevant responses that might come in before it.
This is what I have so far, but I'm not particularly happy with this approach:
Server
// Lots of other code
redis.psubscribe('*');
redis.on("pmessage", function(pattern, channel, message) {
// broadcast
});
io.on('connection', function(client) {
client.on('message', function(message) {
switch(message.method) {
// call relevant function
}
});
});
function object_exists(object_id) {
// do stuff to check object exists
client.send({method: 'object_exists', value: object_exists});
}
Client
var call = Array();
$(document).ready(function() {
socket.connect();
socket.on("message", function(obj){
console.log(obj);
call[obj.method](obj.value);
});
});
function object_exists(object_id) {
socket.send({method: 'object_exists', value: object_id});
// Set a function to be called when the next server message with the 'object_exists' method is received.
call['object_exists'] = function(value) {
if(value) {
// object does exist
}
}
}
tl;dr: I need to 'ask' the server something and then deal with the response using Socket.IO.
You don't specifically say why you are unhappy with your approach, but it looks to me like you are almost there. I am not really sure what you are trying to do with the call array, so I just took it out for clarity.
Basically, you just need to set up a switch statement to act as a message router on each side of the socket connection and fire off the appropriate methods based in incoming messages. Send enough state with the message itself so you can handle the work without any additional context. In your reworked code, I send the object_id to the server and back again to the client.
///SERVER
// Lots of other code
redis.psubscribe('*');
redis.on("pmessage", function(pattern, channel, message) {
// broadcast
});
io.on('connection', function(client) {
client.on('message', function(message) {
switch(message.method) {
case 'object_exists':
object_exists(message.objectId);
break;
}
});
});
//Takes an id an returns true if the object exists
function object_exists(object_id) {
// do stuff to check object exists
client.send({method: 'object_exists', objectId: object_id, value: object_exists});
}
///CLIENT
$(document).ready(function() {
//setup the message event handler for any messages coming back from the server
//This won't fire right away
socket.on("message", function(message){
switch(message.method) {
case 'object_exists':
object_exists(message.objectId, message.value);
break;
}
});
//When we connect, send the server the message asking if object_exists
socket.on("connect", function() {
socket.send({method: 'object_exists', objectId: object_id});
});
//Initiate the connection
socket.connect();
});
//Get's called with with objectId and a true if it exists, false if it does not
function object_exists(objectId, value) {
if(value) {
// object does exist, do something with objectId
}
else {
// object does not exist
}
}
If you want to see a bunch more code in the same stack doing work similar to what you are trying to accomplish, check out my nodechat.js project.

Resources