Spring MVC, best practice how to often polling server - java

Im working on web application using the following stack of technologies: Spring, Hibernate, JSP. I have a task to make one of user social element - messages. As standard of implementation message system i take facebook system. On of the problem i faced is a polling server every 1-5 seconds (what period i have to take?) to retrieve an information about unread messages. Also i want to polling server to retrieve new messages at conversation page (like a chat). What i did:
Example code of get count unread messages.
Server side:
#RequestMapping(value = "/getCountUserUnreadMessages", method = RequestMethod.POST)
public #ResponseBody Callable<Integer> getCountUserUnreadMessages(#ActiveUser final SmartUserDetails smartUserDetails) {
// TODO add additional security checks using username and active user
return new Callable<Integer>() {
#Override
public Integer call() throws Exception {
Integer countUserUnreadMessages = messageService.findCountUserUnreadMessages(smartUserDetails.getSmartUser());
while (countUserUnreadMessages == 0) {
Thread.sleep(1000);
countUserUnreadMessages = messageService.findCountUserUnreadMessages(smartUserDetails.getSmartUser());
}
return countUserUnreadMessages;
}
};
}
Client side:
(function poll(){
setTimeout(function () {
$.ajax({
type: "post",
url: "/messages/getCountUserUnreadMessages",
cache: false,
success: function (response) {
$("#countUnreadMessages").text(response);
}, dataType: "json", complete: poll, timeout: 1000 });
}, 3000);
})();
So client send a request to retrieve count unread messages every second with a timeout in 3 seconds (is it good decision?).
But i think that is the worst callable code ever :-P
Tell me how to do better? What technique use?
Additional information:
Yeah, i supposed that it would be highload, many users service in the Internet.

Try Spring 4 WebSocket support:
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html
WebSockets support full duplex communication over a dedicated TCP connection that you establish over HTTP.

If you are expecting this application to have to scale at all I would make that timing interval more like every 30 - 90 seconds. Otherwise you are basically going to be designing your own built in DOS attack on your self.
You might look into Spring Sockets. It sounds like it's long polling option might work better for you.

Related

Spring Webflux how to keep subsciption alive after reciving last element from repository

I want to create an endpoint that will be streaming data to the client (React app) until the client won't say 'ok, I'm done'. The data is a list of user events that are fetched from mongo.
Get endpoint:
#GetMapping(produces = [MediaType.TEXT_EVENT_STREAM_VALUE])
fun streamEvents(#RequestParam userId: String): Flux<UserEvent> {
return userEventRepository.findAllByUserId(userId)
}
Current implementation closes subscription when receiving the last element. I don't want to keep that connection alive for a long time. I guess it will be 30sec up to 1min. When the connection is active client will be creating new events using POST endpoint.
#PostMapping
fun createUserEvent(#RequestBody userEventRequest: UserEventRequest): Mono<UserEvent> {
return userEventRepository.save(UserEvent(userEventRequest.userId))
}
This is just an example to make it simpler. Instead of calling save on repo I want to make it async so there will be an event fired, handler will do some calculations and save UserEvent to the database. And now I want display that created event on the client side. Can I achieve that using event stream or maybe I need use something different like websockets? The perfect solution will be something like this
#GetMapping(produces = [MediaType.TEXT_EVENT_STREAM_VALUE])
fun streamEvents(#RequestParam userId: String): Flux<UserEvent> {
return userEventRepository.findAllByUserId(userId).takeUntil { it.status != "FINISHED" }
}
I have previously used ServerSentEventsfor this type of logic and keeping the connection alive by sending keep alive messages during a given interval.
Here is a code example from one of my previous projects, where i merge in the keep alive messages alongside the regular events.
.GET("", accept(TEXT_EVENT_STREAM), request -> ok()
.contentType(TEXT_EVENT_STREAM)
.header("Cache-Control", "no-transform")
.body(Flux.merge(myHandler.getEvents()),
Flux.interval(Duration.ofSeconds(15))
.map(aLong -> ServerSentEvent.builder()
.comment("keep alive")
.build())),
new ParameterizedTypeReference<List<MyEvents>>() {}))

Spring - server.connection-timeout not working

In my application.properties file I have...
server.port=8086
server.connection-timeout=15000
I know that the file is being loaded correctly because the server is running on port 8086.
In the application I have a RestController
#RestController
class TestController {
#GetMapping()
fun getValues(): ResponseEntity<*> {
return someLongRunningProcessPossiblyHanging()
}
}
When I call the endpoint, the request never times out, it just hangs indefinitely.
Am I missing something?
NOTE: I've also been informed that Tomcat uses this field in minutes, not milliseconds (rather unusual choice IMO). I've tried setting this to server.connection-timeout=1 denoting 1 minute, but this didn't work either.
NOTE: I don't want another HTTP request to cause the previous request to time out, I want each HTTP request to timeout of it's own accord, should too much time elapse to serve the request.
connection-timeout does not apply to long running requests. It does apply to the initial connection, when the server waits for the client to say something.
Tomcat docs (not Spring Boot) define it as The number of milliseconds this Connector will wait, after accepting a connection, for the request URI line to be presented [...]
To test the setting server.connection-timeout=4000 I connect using netcat and I don't send any HTTP request/headers. I get:
$ time nc -vv localhost 1234
Connection to localhost 1234 port [tcp/*] succeeded!
real 0m4.015s
user 0m0.000s
sys 0m0.000s
Alternatives
1) Async
From brightinventions.pl - Spring MVC Thread Pool Timeouts:
In Spring MVC there is no way to configure a timeout unless you use async method. With async method one can use spring.mvc.async.request-timeout= to set amount of time (in milliseconds) before asynchronous request handling times out.
I've set spring.mvc.async.request-timeout=4000 and I get a timeout in the browser with this:
#GetMapping("/test-async")
public Callable<String> getFoobar() {
return () -> {
Thread.sleep(12000); //this will cause a timeout
return "foobar";
};
}
See Spring Boot REST API - request timeout?
2) Servlet filter
Another solution would be to use a servlet filter brightinventions.pl - Request timeouts in Spring MVC (Kotlin):
override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain) {
val completed = AtomicBoolean(false)
val requestHandlingThread = Thread.currentThread()
val timeout = timeoutsPool.schedule({
if (completed.compareAndSet(false, true)) {
requestHandlingThread.interrupt()
}
}, 5, TimeUnit.SECONDS)
try {
filterChain.doFilter(request, response)
timeout.cancel(false)
} finally {
completed.set(true)
}
}
3) Tomcat Stuck Thread Detection Valve?
Tomcat has a Stuck Thread Detection Valve but I don't know if this can be configured programmatically using Spring Boot.
From the official docs:
server.connection-timeout= # Time that connectors wait for another HTTP request before closing the connection. When not set, the connector's container-specific default is used. Use a value of -1 to indicate no (that is, an infinite) timeout.
Another ref, also mentions the same. It should work for you.
When I call the endpoint, the request never times out, it just hangs indefinitely.
server.connection-timeout isn't a request timeout. It is a timeout for idle connections, i.e. those that have already had a request/response pair and on which the server is now awaiting a second request. It is essentially a server-side read timeout.

jquery/angular scheduler implementation

I have a chat application written on jquery. When user sends a message, this message is being sent to java REST endpoint. But I also want to listen for response. I think it would be good to listen other REST endpoint every second, because I have no idea how to implement it like in Facebook. So if there new message, get them and show. How to do that in jquery?
There are couple of solutions for that, but I'd suggest you do not create chat application using REST. If you want pure JS think about firebase from google (it will be realtime)/meteor or definitely using Websockets (you'll need a websocket server and you can use authobahnjs to subscribe to that server).
I do realize it's solution that's more complicated than REST, but if you ask me and probably your customers REST is really not the best choice for chat/realtime applications...
Just think of situation where you will have lots of users online and each of them will not only be sending a lot of queries (if they write fast) but as well checking for response every second. Now think if something goes wrong on the network what will happen with your server... you'll get tons of 'hanging' connections etc.
btw. ppl who tried it (like me in past) will tell you the same, no chats with polling for messages over REST! ;)
JQuery way...
Ok, just watch out for defining multiple events on the same element (that’s classic one when working with Jquery), here’s a function that should do what you want (its a post, but you can adapt it):
var interval = 1000; // 1000 = 1 second, 3000 = 3 seconds
function doAjax() {
$.ajax({
type: 'POST',
url: 'chatController.php',
data: {action: 'getMessagesForUser', user: {user: 'data'}},
dataType: 'json',
success: function (data) {
//do something with results
},
complete: function (data) {
// Schedule the next
setTimeout(doAjax, interval);
}
});
}
setTimeout(doAjax, interval);
You could also introduce some variable that would make sure you're not making another request before the previous one has completed etc.

Long polling freezes browser and block other ajax request

I am trying to implement long polling in my Spring-MVC Web App but it freezes my browser and other request after 4-5 continues AJAX requests.I have no clue whats goin on here is my relevant code.
The controller method:(Server Side):-
#Asynchronous
#RequestMapping("/notify")
public #ResponseBody
Events notifyEvent(HttpServletRequest request) {
Events events = null;
try {
events = (Events) request.getSession(false).getServletContext().getAttribute("events");
System.out.println("Request Came from" + ((com.hcdc.coedp.safe.domain.User) request.getSession(false).getAttribute(Constants.KEY_LOGGED_IN_USER)).getLoginId());
if (!events.getTypeOfEvents().isEmpty()) {
System.out.println("Removing older entries");
events.getTypeOfEvents().clear();
}
while (!events.isHappend()) {
//Waiting for event to happen.
}
events = Events.getInstance();
events.setHappend(false);
request.getSession(false).getServletContext().setAttribute("events", events);
}catch (Exception e) {
e.printStackTrace();
}
return events;
}
The long-polling script(Client Side):-
$(document).ready(function() {
$.ajaxSetup({
async:true//set a global ajax requests as asynchronus
});
alert('Handler for .onload() called.');
waitForMsg();
});
function waitForMsg(){
xhr= $.ajax({
type: "POST",
url: '<%=request.getContextPath()%>/notification/notify',
async: true, /* If set to non-async, browser shows page as "Loading.."*/
cache: false,
timeout:50000, /* Timeout in ms */
global:false,
success: function(data){ /* called when request to notifier completes */
/* Doing smthing with response **/
setTimeout(
waitForMsg, /* Request next message */
1000 /* ..after 1 seconds */
);
},
error: function(XMLHttpRequest, textStatus, errorThrown){
addmsg("error", textStatus + " (" + errorThrown + ")");
setTimeout(
waitForMsg, /* Try again after.. */
15000); /* milliseconds (15seconds) */
}
});
};
UPDATE:
function updateFeed(event, data) {
var f=eval(data);
alert(f.typeOfEvents.length);
}
function catchAll(event, data, type) {
console.log(data);
alert("error");
console.log(type);
}
$.comet.connect('<%=request.getContextPath()%>/notification/notify');
$(document).bind('feed.comet', updateFeed);
$(document).bind('.comet', catchAll);
Neither alert box pops up..:(
Seems like you experienced the session file lock
For PHP
Use session_write_close() when you don't need session value
It seems you have an empty while loop in your browser code.. this is a very CPU instensive way to wait for an event.
If no events happen the client will kill the request after your desired timeout of 50 seconds. But I'm not sure if the server thread is killed too, or if it "whiles" on forever (unless there is an event). The next request will start a second server thread that hangs in the while loop too then. Maybe the amount of empty while loops is an overkill for the server, so that it stops accepting any more requests. So after some requests (that each triggered an endless server thread) the client waits forever on a new request.. because it can't be handled by the server.
ps: on success you commented to wait 1 second, but set the timeout to 10000 (10 seconds)
I've met similar problem, my browser was stucked somehow with AJAX requests. Hint: instead using waitForMsg() directly, try setTimeout("waitForMsg()",10).
FYI, here is a project that might help you: https://github.com/SeanOC/jquery.comet
In general, I would search for JavaScript comet APIs that can support web sockets if available on client / server with graceful fallback to long polling. The API should handle all the gory details, allowing you to focus on the application.
Here's a link to an old dojo article on the topic: http://dojotoolkit.org/features/1.6/dojo-websocket
Good luck.
You can try to rewrite the behaviour using jQuery deferred:
function setShortTimeout() {
setTimeout(waitForMsg, 1000);
}
function setLongTimeout() {
setTimeout(waitForMsg, 15000);
}
$(document).ready(function() {
$.ajaxSetup({
async:true//set a global ajax requests as asynchronus
});
alert('Handler for .onload() called.');
$.when(waitForMsg())
.done(successHandler, setShortTimeout)
.fail(errorHandler, setLongTimeout);
});
function waitForMsg(){
return $.ajax({
type: "POST",
url: '<%=request.getContextPath()%>/notification/notify',
async: true, /* If set to non-async, browser shows page as "Loading.."*/
cache: false,
timeout:50000, /* Timeout in ms */
global:false
});
};
errorHandler and successHandler will be your success: and error: callbacks, which I omitted for clarity, with their setTimeout part removed (since it is now part of the deferred.done() and .fail() callbacks).
Let me know if it works.
I am a PHP developer but I met your problem and it could be the same behaviour. So I give you my 2 cents and hope it'll help you.
The line that does make me suspect a problem is :
events = (Events) request.getSession(false).getServletContext().getAttribute("events");
In PHP, sessions are stored in files, and if we are long-polling on a php script while the session is open, we meet a race condition problem.
The principle is quite simple :
When a request opens the session, file is locked until the session
is closed.
If other requests comes to the server, they will be locked until the
session is released from the previous request.
In a case of long polling, if the session is opened and not closed just after getting information (at least, just before waiting for events), all requests are just locked, you can't go anywhere else in the website if you're using sessions on other pages. Even if you open a new tab, because for one browser there is only one session, you're locked.
It may be this:
xhr= $.ajax({ (...)
in your waitForMsg function.
Try
var xhr = (...)
It may be that you are declaring xhr in the global object, thus making it impossible to respond to two different requests.

long polling using JSON not working

I am trying to build chat application with long polling mechanism on Google app engine server.
HTTPRequest has default time out of 30 seconds, so I am sending polling request to server every 28 seconds if there is no update from server (so that I wont miss any message from other clients).
First request gets registered, but second request sent after 28 seconds is not reaching server.
function loadPage(query){
$.get({ url: query, success: function(events){
updated = 1;
//events data processing
createServerChannel();
});
}
function createServerChannel(){
var query='/ChatController?&user='+userName+'&sessionName='+sessionName+'&register=true';
loadPage(query);
updated = 0;
setInterval(function() { poll(query); }, 28000);
};
function poll(query){
if(updated==0){
loadPage(query);
}
}
I am using thread.wait() for request to wait on server. Is there any way to consume first pending request when next request from same client is available.
Please help.
I think web sockets might be a better approach as this keeps a continuous connection open to the server and waits for the server to push data to the client.
http://www.html5rocks.com/en/tutorials/websockets/basics/

Categories

Resources