Use case:
I have to handle several events which require an "available client". So in each event handler I first have to try to get an available client. If there is no client available I'll respond with a "Service unavailable" message. Right now I've implemented that requirement like this:
public constructor(consumer: RpcConsumer) {
consumer.on('requestA', this.onRequestA);
}
private onRequestA = async (msg: RpcConsumerMessage) {
const client: RequestClient = this.getClient(msg);
if (client == null) {
return;
}
msg.reply(await client.getResponseA());
}
private getClient(msg: RpcConsumerMessage): RequestClient {
const client: RequestClient= this.clientManager.getClient();
if (client == null) {
const err: Error = new Error('Currently there is no client available to process this request');
msg.reply(undefined, MessageStatus.ServiceUnavailable, err);
return;
}
return client;
}
The problem:
I don't want to check for an available client in all event handlers again and again. Instead I thought a middleware would perfectly fit into this use case. It would check for an available client and passes on the client instance if there is one. If there is not available client it will respond with the error message.
The question:
How would I write such a middleware for this case?
Build a curried method for this:
private withClient(cb: (client: RequestClient) => string | Promise<string>) {
return function(msg: RpcConsumerMessage) {
const client: RequestClient= this.clientManager.getClient();
if (client == null) {
const err: Error = new Error('Currently there is no client available to process this request');
msg.reply(undefined, MessageStatus.ServiceUnavailable, err);
return;
}
msq.reply(await cb(client));
};
}
So you can use it as:
private onRequestA = withClient(client => client.getResponseA());
If I understand correctly I don't think you actually NEED middleware, although you might choose to go that route.
You can just have a module that is in charge of finding a client and serving one up if it is available. This would look something like this:
const _client;
module.exports = {
getClient
}
getClient(){
return _client;
}
function setClient(){
//Your logic to find an available client
//run this whenever a client disconnects (if there is an event for that) or until you are connected to a client
_clent = client; //When you find that client set it to `_client`. You will return this everytime someone calls getClient.
}
The advantage here is that once you find a client, the module will serve up that same client until you are disconnected from it. The trick then is just making sure that you are always trying to connect to client when you are disconnected - even when there are no requests. I hope this makes sense.
Related
Is there a strategy that would work within the current Firebase offering to detect if the server connection is lost and/or regained?
I'm considering some offline contingencies for mobile devices and I would like a reliable means to determine when the Firebase data layer is available.
This is a commonly requested feature, and we just released an API update to let you do this!
var firebaseRef = new Firebase('http://INSTANCE.firebaseio.com');
firebaseRef.child('.info/connected').on('value', function(connectedSnap) {
if (connectedSnap.val() === true) {
/* we're connected! */
} else {
/* we're disconnected! */
}
});
Full docs are available at https://firebase.google.com/docs/database/web/offline-capabilities.
Updated:
For many presence-related features, it is useful for a client to know when it is online or offline. Firebase Realtime Database clients provide a special location at /.info/connected which is updated every time the client's connection state changes. Here is an example:
DatabaseReference connectedRef = FirebaseDatabase.getInstance().getReference(".info/connected");
connectedRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
boolean connected = snapshot.getValue(Boolean.class);
if (connected) {
System.out.println("connected");
} else {
System.out.println("not connected");
}
}
#Override
public void onCancelled(DatabaseError error) {
System.err.println("Listener was cancelled");
}
});
I guess this changed in the last couple of months. Currently the instructions are here:
https://firebase.google.com/docs/database/web/offline-capabilities
In summation:
var presenceRef = firebase.database().ref("disconnectmessage");
// Write a string when this client loses connection
presenceRef.onDisconnect().set("I disconnected!");
and:
var connectedRef = firebase.database().ref(".info/connected");
connectedRef.on("value", function(snap) {
if (snap.val() === true) {
alert("connected");
} else {
alert("not connected");
}
});
I'll admit I don't know a lot about how references are set, or what that means (are you making them out of thin air or do you have to have already created them beforehand?) or which one of those would trigger something on the server as opposed to something on the front end, but if the link is still current when you read this, a little more reading might help.
For android you can make user offline by just a single function called onDisconnect()
I did this in one of my chat app where user needs to get offline automatically if network connection lost or user disconnected from Internet
DatabaseReference presenceRef = FirebaseDatabase.getInstance().getReference("USERS/24/online_status");
presenceRef.onDisconnect().setValue(0);
On disconnecting from network Here I am making online_status 0 of user whose Id is 24 in firebase.
getReference("USERS/24/online_status") is the path to the value you need to update on offline/online.
You can read about it in offline capabilities
Note that firebase takes time around 2-10 minutes to execute onDisconnect() function.
firebase for web
firebase.database().ref(".info/connected").on("value",(snap)=> {});
The suggested solution didn't work for me, so I decided to check the connection by writing and reading 'health/check' value. This is the code:
const config = {databaseURL: `https://${projectName.trim()}.firebaseio.com/`};
//if app was already initialised delete it
if (firebase.apps.length) {
await firebase.app().delete();
}
// initialise app
let cloud = firebase.initializeApp(config).database();
// checking connection with the app/database
let connectionRef = cloud.ref('health');
connectionRef.set('check')
.then(() => {
return connectionRef.once("value");
})
.then(async (snap) => {
if (snap.val() === 'check') {
// clear the check input
await connectionRef.remove();
// do smth here becasue it works
}
});
I have a node application handling some ZeroMQ events coming from another application utilizing the Node-ZMQ bindings found here: https://github.com/JustinTulloss/zeromq.node
The issue I am running into is one of the operations from an event takes a long time to process and this appears to be blocking any other event from being processed during this time. Although the application is not currently clustered, doing so would only afford a few more threads and doesn't really solve the issue. I am wondering if there is a way of allowing for these async calls to not block other incoming requests while they process, and how I might go about implementing them.
Here is a highly condensed/contrived code example of what I am doing currently:
var zmq = require('zmq');
var zmqResponder = zmq.socket('rep');
var Client = require('node-rest-client').Client;
var client = new Client();
zmqResponder.on('message', function (msg, data) {
var parsed = JSON.parse(msg);
logging.info('ZMQ Request received: ' + parsed.event);
switch (parsed.event) {
case 'create':
//Typically short running process, not an issue
case 'update':
//Long running process this is the issue
serverRequest().then(function(response){
zmqResponder.send(JSON.stringify(response));
});
}
});
function serverRequest(){
var deferred = Q.defer();
client.get(function (data, response) {
if (response.statusCode !== 200) {
deferred.reject(data.data);
} else {
deferred.resolve(data.data);
}
});
return deferred.promise;
}
EDIT** Here's a gist of the code: https://gist.github.com/battlecow/cd0c2233e9f197ec0049
I think, through the comment thread, I've identified your issue. REQ/REP has a strict synchronous message order guarantee... You must receive-send-receive-send-etc. REQ must start with send and REP must start with receive. So, you're only processing one message at a time because the socket types you've chosen enforce that.
If you were using a different, non-event-driven language, you'd likely get an error telling you what you'd done wrong when you tried to send or receive twice in a row, but node lets you do it and just queues the subsequent messages until it's their turn in the message order.
You want to change REQ/REP to DEALER/ROUTER and it'll work the way you expect. You'll have to change your logic slightly for the ROUTER socket to get it to send appropriately, but everything else should work the same.
Rough example code, using the relevant portions of the posted gist:
var zmqResponder = zmq.socket('router');
zmqResponder.on('message', function (msg, data) {
var peer_id = msg[0];
var parsed = JSON.parse(msg[1]);
switch (parsed.event) {
case 'create':
// build parsedResponse, then...
zmqResponder.send([peer_id, JSON.stringify(parsedResponse)]);
break;
}
});
zmqResponder.bind('tcp://*:5668', function (err) {
if (err) {
logging.error(err);
} else {
logging.info("ZMQ awaiting orders on port 5668");
}
});
... you need to grab the peer_id (or whatever you want to call it, in ZMQ nomenclature it's the socket ID of the socket you're sending from, think of it as an "address" of sorts) from the first frame of the message you receive, and then use send it as the first frame of the message you send back.
By the way, I just noticed in your gist you are both connect()-ing and bind()-ing on the same socket (zmq.js lines 52 & 143, respectively). Don't do that. Inferring from other clues, you just want to bind() on this side of the process.
I am currently building an api application that checks the status and gets information of various types of dbs(i.e. Mongo, MySQL) using Sailsjs such as users, depending on a user input. Here is a snippet of the code I am working on. The local host is just the test database I am connecting to, but in the future it will be supplied by the user.
var mp = require('mongodb-promise');
var MongoClient = require('mongodb');
mp.MongoClient.connect("mongodb://#localhost:27017/test")
.then(function(db){
db.getUsers().then(function(users){
res.ok(users);
})
})
.fail(function(err) {
console.log(err);
})
I am attempting to use promises for the async issue. The problem I am having is that it doesn't work. It tells me that that Object[object object] has no method 'getUsers'. I have searched and can't seem to find a solution that works.
If I change the function to the below, I get the some data back.
mp.MongoClient.connect("mongodb://#localhost:27017/IMS")
.then(function(db){
db.stats().then(function(stats){
return res.ok(stats);
})
})
.fail(function(err) {
console.log(err);
dbObject.vipUp = false;
})
I am not sure what the issue is or how to solve it.
What you are doing here is using the node native driver methods to connect and inspect the database. There is in fact "no such method" as .getUsers() here in this API or in fact in any other API.
The .getUsers() function is just a "shell helper" that is basically implemented like this:
function (args) {
var cmdObj = {usersInfo: 1};
Object.extend(cmdObj, args);
var res = this.runCommand(cmdObj);
if (!res.ok) {
var authSchemaIncompatibleCode = 69;
if (res.code == authSchemaIncompatibleCode ||
(res.code == null && res.errmsg == "no such cmd: usersInfo")) {
// Working with 2.4 schema user data
return this.system.users.find({}).toArray();
}
throw Error(res.errmsg);
}
return res.users;
}
So what you should be able to see here is that this normally wraps a "command" form, or otherwise falls back for compatibility with MongoDB 2.4 to querying the system.users collection on the current database.
Therefore, instead of calling a method that does not exist, you then need to use the .command() method instead:
mp.MongoClient.connect("mongodb://#localhost:27017/test")
.then(function(db){
db.command({ "usersInfo": 1}).then(function(users){
res.ok(users);
})
})
.fail(function(err) {
console.log(err);
})
Or in the case of connecting to a MongoDB 2.4 instance, then fetch from the .collection():
mp.MongoClient.connect("mongodb://#localhost:27017/test")
.then(function(db){
db.collection('system.users').find().toArray().then(function(users){
res.ok(users);
})
})
.fail(function(err) {
console.log(err);
})
At any rate, you really should be establishing the database connection elsewhere in your application ( or re-using the underlying driver connection from another store ), and then calling methods on the connection already establihed. This is always preferable to creating a connection on the request of the information you want to retrieve.
Also, recent versions of the node native driver support promises right out of the box. So there may be no need to configure in anything else, depending on how you intend to use it.
I have tried the following typical approaches but couldn't able to do a redirect from an asynchronous ASP.NET method:
Response.Redirect("~/Login.aspx");
HttpContext.Current.Response.Redirect("~/Login.aspx");
I have also tried Server.Transfer but couldn't get success due to the unavailability of the page controls inside a reference method(delegate).
I have already tried a static property which I filled in delegate response and continuously checking it on client side using ASP.NET SignalR to perform a redirect but as it a static property, it redirects all the user to the login page which I don't want to do it.
private void Response_Recieved(Message objMessage)
{
try
{
if (objMessage.OperationType == Operation.Data)
{
NotificationMessage objNotifications = new DataProcess().Deserialize_Messages(objMessage);
_jsonData = JsonConvert.SerializeObject(objNotifications);
}
else if (objMessage.OperationType == Operation.ServerAbnormalDisconnect)
{
// I want to redirect a user to login page whenever server disconnects
HttpContext.Current.Response.Redirect("~/Login.aspx");
//Response.Redirect("~/Login.aspx");
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
What would be the alternative or best approach to perform a redirect from a delegate function when there is no control available also without using any static property?
You can't issue a HTTP redirect call from an asynchronous operation in ASP.Net but there are viable alternatives. I will make my answer generic to hopefully help other readers but as a SignalR user you need to look at number 3.
Let's examine 3 scenarios:
An async operation is commenced from within a normal HTTP request using HostingEnvironment.QueueBackgroundWorkItem (.NET 4.5.2 onwards).The resource requested is (where applicable) processed/rendered, and returned. The request is completed. There is no longer any request to redirect. In this scenario, you could perhaps store a value in the Application cache with an expiry time to redirect the user on the next request.
Your clients are connected by way of web socket and your server side implementation uses Microsoft.WebSockets.dll. The connection to the web server is upgraded to a full-duplex socket connection; it is not the url for the page but a comms connection so there is nothing to redirect.Instead, you send a command over the connection informing the client side code that a redirect is needed and you perform the redirect in JavaScript. In the WebSocketHandler, with this example sending a string command:
Send("LOGOFF");
and in the JavaScript ws.onmessage handler, identify the message as "LOGOFF" and change the window.location.href to the target page:
ws.onmessage = function (message) {
switch (message.data) {
case "LOGOFF":
location.href = "Login.aspx";
}
};
The above example is simplified. I have a site which does this and actually send a class (JSON serialised) with a command type and optional payload.
SignalR has the same issues as #2 and I would propose a similar solution. I've not worked with SignalR yet but according to a comment on this answer you would send the command like so:
GlobalHost.ConnectionManager.GetHubContext<Chat>().Clients.Client(connectionId).addMessage("LOGOFF");
Look out for the LOGOFF message in your SignalR client-side message handler and set the window location href to your login page.
The Response.Redirect method uses a ThreadAbortException to stop the execution of the current request.
As you are catching that exception and just absorbing it, the request handling will just go on as usual and ignore the redirect that you tried to do.
You can use a variable to flag your desire to do the redirect, and then perform it outside the try...catch:
private void Response_Recieved(Message objMessage) {
bool doRedirect = false;
try {
if (objMessage.OperationType == Operation.Message_Response) {
NotificationMessage objNotifications = new DataProcess().Deserialize_Messages(objMessage);
_jsonData = JsonConvert.SerializeObject(objNotifications);
} else if (objMessageBo.OperationType == Operation.ServerAbnormalDisconnect) {
// I want to redirect a user to login page whenever server disconnects
doRedirect = true;
}
} catch (Exception ex) {
Logger.WriteException(ex);
}
if (doRedirect) {
HttpContext.Current.Response.Redirect("~/Login.aspx");
}
}
Here is what I had done years back. I am posting it here to help others who are asking me.
Well it is clearly can't be done from an asynchronous ASP.NET method(delegate).
So to achieve the desire functionality I passed a value to the client side from the method using normal broadcasting in SignalR.
And on client side, I am validating and performing actions accordingly. I have changed the URL using simple JavaScript. I am putting a simplest code to understand the core concept of redirection from ASP.NET using SignalR.
Code Behind
[HubMethodName("getSessionState")]
public string GetSessionState(string status) {
return Clients.Caller.UpdatedState(status);
}
private void Response_Recieved(Message objMessage)
{
try
{
if (objMessage.OperationType == Operation.Data)
{
NotificationMessage objNotifications = new DataProcess().Deserialize_Messages(objMessage);
_jsonData = JsonConvert.SerializeObject(objNotifications);
SendNotifications(_jsonData);
}
else if (objMessage.OperationType == Operation.ServerAbnormalDisconnect)
{
GetSessionState("false"); //<--- Disconnecting session by passing false for sessionstate
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
var notifications = $.connection.notificationsHub;
notifications.client.updatedState = function (status) {
if (status === "false") {
window.alert("Server is disconnected. Forced logout!");
window.location.href = "/logout.aspx"
}
else {
// Doing my stuff here...
}
};
I am using realtime.co for realtime messaging in my .NET 4.5/Javascript webapp.
I created a connection using the code:
xRTML.ready(function () {
xRTML.Config.debug = true;
globalRealtimeConnectionId = generateUUID();
globalRealtimeToken = getRealtimeToken();
globalMyConnection = xRTML.ConnectionManager.create(
{
id: globalRealtimeConnectionId,
appkey: 'xxxx',
authToken: globalRealtimeToken, // insert token
url: 'http://ortc-developers.realtime.co/server/2.1'
});
globalMyConnection.bind(
{
// When we get a message, process it
message: function (e) {
var user = e.message; // the use that just joined
}
});
globalMyConnection.active = true;
});
On the server I gave permissions to "main:*" (all sub channels) and returned a token.
When I send a message to the user from the server using the following code:
OrtcClient client = (OrtcClient)Application["realtime"]; // get reference to client, which initialized in global.asax
client.Send(channel, user); // send message
user is a string with the username, channel is the channel name (e.g. main:12_323_34_. I get the following error in xrtml-custom-3.2.0-min.js:1
Uncaught TypeError: boolean is not a function xrtml-custom-3.2.0-min.js:1
c.Connection.process
(anonymous function)
f.proxy
IbtRealTimeSJ.d.sockjs.d.sockjs.onmessage
x.dispatchEvent
m._dispatchMessage
m._didMessage
m.websocket.d.ws.onmessage
From what I can tell, the client is subscribed because it triggers something when a message is sent to it from the server. But I can't understand the source of the error. Because of the error, the function binded to "message:" is not triggered.
For debugging purposes, if I were you, I would include the non-minified version so you can see exactly what function is causing the error. Once you've done that, it should be easier to track down.
One other quick note is when making RESTful calls you should try to do this in the back end so your api key is not exposed to the public. This would obviously only be an issue when you are creating a public facing website, so if this is an organizational (internal) application you can disregard.