I'm working on a real-time chat. I need to change statuses of the room owner and connected users, together with the UI. Since NodeJS/SocketJS/etc don't guarantee message delivery, I switched to pure Ajax for that.
The system works like that:
- User presses a button to change his status
- An Ajax request is being sent to the server, and a status change request is being saved in the queue in DB
- Users send Ajax heartbeats every second. On the server this heartbeat function also processes the queue (when sent by the room owner). Besides it sends the current statuses of users in the room in response every time.
The issue is: there might be temporary internet problems on both sides, which causes all kinds of problems. This happens due to the fact, that the heartbeat Ajax requests are being processed in an arbitrary order on the server, or the responses are being received in a wrong order on the client side. As a result users have wrong data about current statuses and the UI changes are also wrong.
What is the best approach when making a system like that? What am I doing wrong or how can I fix the issues above?
Thank you!
Have a look at Max's blog a Django-Realtime-Chat and how he does it.
Related
I'm working on a chat room program using ASP.NET, I have made a 'user has joined the chatroom' message, but how would I send a message when the user leaves the page? I tried looking through the network logs, but I can't see any extra messages getting sent through the WebSocket.
This will be tough and inaccurate because there is really no guarantee that you will get notified that the user has disconnected. But there is an onDisconnect event that you can listen for, but in all honesty, I would use a disconnect button and use that to do what you need to do, it would be more consistent, as the disconnect is not reliable (at least the last time I used SignalR, which was like version 1.x)
Nevermind, I've found out about window.onbeforeunload using JavaScript. Though it's not 100% consistent, it's more consistent than I expected.
I have an application which allow an user to send a lot of SMS to his contacts (like thousands).
Obviously that tasks can take a lot of time to complete.
So the idea is to display a progress bar on the client side, to indicate the user how many messages have been sent so far.
The back end of my app is a restful spring webservice.
The front end is done with ReactJS and Redux.
The question is:
Is it technically possible from the back end to periodically push data to the client, to update the progress bar, with the amount of messages already sent.
First question regarding the back end architecture:
I've seen that using JAX-RS 2 with spring, I can make asynchronous call in the back end, to execute other tasks(like querying the DB to see the messages already sent) while the other process is sending all messages. Am i looking in the right direction here ?
Second question regarding front end :
So far I use thunk functions for my requests(post/get) to the server, which returns a json response, and it works well. But in this case, the back end would periodically push data to the client side, until the main task is completed, so I don't understand how would that work out exactly ?
I guess I'm not gonna be able to achieve that using the same request ? Should I look at other technologies to achieve that ?
Please let me know, if the description of my problem is not clear.
Cheers
There are two options: you can keep track of the task on the backend and have an API endpoint to check the status and poll it every x seconds.
The other option would be to use sockets, the frontend client would listen for an event and update display onEvent. The backend would be responsible for emitting events.
Let suppose the following simple UC based on a CQRS architecture:
We have a backend managing a Business Object, let says a Movie.
This backend is composed of 2 Microservices: a CommandManager (Create/Update/Delete Movie) and a QueryManager (Query Movie)
We have a frontend that offer a web page for creating a new Movie and this action lead automatically to another web page describing the Movie.
A simple way to do that is:
A web page collect movie information using a form and send them to the frontend.
The frontend make a POST request to the CommandManager
The CommandManager write the new movies to the datastore and return the movie key
The frontend make a GET using this key to the QueryManager
The QueryManager looks for the Movie in the Datastore using the key and return it.
The frontend deliver the page with the Movie Information.
Ok, now I want to transform this UC in a more Event Driven way. Here is the new flow:
A web page collect movie information using a form and send them to the frontend.
The frontend write a Message in the BUS with the new movie information
The CommandManager listen the BUS and create the new movies in the datastore. Eventually, it publish a new message in the BUS specifying that a new Movie has been created.
At this point, the frontend is no more waiting for a response due to the fact that this kind of flow is asynchronous. How could we complete this flow in order to forward the user to the Movie Information Web page? We should wait that the creation process is done before querying the QueryManager.
In a more general term, in a asynchronous architecture based on bus/event, how to execute Query used to provide information in a web page?
In addition to #VoiceOfUnreason's answer,
If the two microservices are RESTFul, the CommandManager could return a 202 Accepted with a link pointing to the resource that will be created in the future. The client could then poll that resource until the server responds with a 200 OK.
Another solution would be that the CommandManager would return a 202 Accepted with a link pointing to a command/status endpoint. The client would poll that endpoint until the status is command-processed (including the URL to the the actual resource) or command-failed (including a descriptive message for the failure).
These solutions could be augmented by sending the status of all processed commands using Server Sent Events. In this way, the client gets notified without polling.
If the client is not aware that the architecture is asynchronous, a solution is to use an API gateway that blocks the client's request until the upstream microservice processes the command and then to respond with the complete resource's data.
At this point, the frontend is no more waiting for a response due to the fact that this kind of flow is asynchronous. How could we complete this flow in order to forward the user to the Movie Information Web page? We should wait that the creation process is done before querying the QueryManager.
Short answer: make the protocol explicit.
Longer answer: a good place to look for inspiration here is HTTP.
The front end makes a POST to the origin server; as a result the origin server places a message on the queue and sends a response back.
The representation sent with this response ought to describe the request's current status and point to (or embed) a status monitor that can provide the user with an estimate of when the request will be fulfilled.
The client can then poll the endpoint to find out what progress has been made.
For instance, the endpoint might be a query into the data store, that looks for evidence that the command manager has processed the original command; or it might be an endpoint that is watching the bus for the MovieCreated message, and changes its answer based on whether or not it has seen that.
It may help clarify things to look into idempotent request handling; when the Command Manager pulls a message off of its queue, how does it know if it has previously processed a copy of that message? Your polling endpoint should be able to use the same information to let the consumer know that the message has been successfully processed.
In addition to #Constantin Galbenu's answer, I would like to put in my two cents.
I would strongly advise you to look at a microservices pattern called "BFF" (Backend-For-Frontend) pattern. Instead of having a thick API gateway doing all the work, you can have an API per use-case. For Example: In your case, you can an API called "CreateMovieBFFHandler" which would receive the POST request from front-end and then this guy would coordinate with other things in the system like message queues, events etc. to track the status of the submitted request. UI might have a protocol with this BFFhandler that if the response doesn't come back in X seconds, then the front-end would consider it as failure and if this handler is able to get a successfully processed messaged from message queue or "MovieCreated" event for this key, then it could send a 200 OK back and then you can redirect the page to call write side and then populate the UI.
Useful Link: https://samnewman.io/patterns/architectural/bff/
For example, when we have new message on http://facebook.com or http://gmail.com, site gives us signal about this, and this happened without refresh page. how it works? I Imagine this so:
some Jhon is registered user on site.
There is javascript code, which every 10 seconds sends AJAX request to messages table, for check: in table, has or not unread messages for john (architecture, how marking ready / unread messages, here not important), if Yes, site give signal to Jhon "You have several new messages".
Tell please, I am closer to the truth or not? If yes, I have also one questions please.
In modern sites we use websockets which enable the server to push information to the browser with less overhead and no delay. That's what's used on SO.
But as some browsers still don't support them, we must sometimes fall back to a pull-loop as the one you describe.
On the server it may be handled with a queue instead of database queries at each browser request. But there are probably many different implementations on this point.
I'm wondering how it's implemented in Gmail, that every time you receive e-mail, the list of mails is automatically refreshed. It looks like the server is sending some kind of event to the browser, but how is it possible? Or maybe it’s simle: the browser ask the server for new messages every let’s say 2 seconds? But it would probably kill the performance…
Anyone have some ideas?
EDIT: OK, so if it's the simple answer, how do they manage performance? When I send an email from an other account to the gmail account the view is "refreshed" almost instantly. You were saying about a simple function that returns true / false, but it must have some logic (db connection or reads some files). How they manage it?
See also: How is GMail Chat able to make AJAX requests without client interaction?
Dont know exactly which technoloy Gmail uses, but the concept is to open a channel - using reverse AJAX, comet or sprocket based techniques.
Think of it as the client requesting the server for data, but the server does not return for one minute unless it has new mail. Using this technique, the client can almost show the results in a real time manner and it does not have to poll every 2 secs. Makes sense?
gmail is, in fact, polling the server for updates. Not as often as every two seconds, though. That would be madness. A bit of testing with Tamper Data makes it look like maybe every 20 seconds, though there seem to be multiple events going through that confuse it a bit.
Regarding your edit, I imagine they might have a last-activity timestamp on the account tracking in their database, with the client polling query retrieving that via Ajax and comparing with its last sync to determine whether it needs to do a full update.
You have right with simple answer. Google Mail checking new messages on server via AJAX.
It must be some kind of ajax listener that get informations every X seconds.
I already set something like that for one of my projects. What I was doing is calling a function that was returning true or false. True if the page needed to be refreshed, false otherwise. Then if you have an update, you do another call to get the actual update. This way you don't have to refresh everything every time... but it's still intense on the server if you have a lot of users.
In other words and like chaos said, it's polling the server.