I created Chat application using SignalR and Asp.net MVC. if user is idle for sometime, he didn't receive any messages from the server.
Is there any timeout for SignalR persistence connection ?
If yes, how do I modify or reactivate my connection ?
This has now been upated on the Wiki https://github.com/SignalR/SignalR/wiki/Configuring-SignalR
Code
using System;
using SignalR;
namespace WebApplication1
{
public class Global : System.Web.HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
// Make connections wait 50s maximum for any response. After
// 50s are up, trigger a timeout command and make the client reconnect.
GlobalHost.Configuration.ConnectionTimeout = TimeSpan.FromSeconds(50);
}
}
}
SignalR's default timeout is 110 seconds for any connection. It will reconnected automatically and you shouldn't miss any messages. That sounds like you have some other problem. If you want to tweak the timeout in 0.4 like this (assuming asp.net):
// Make idle connections reconnect every 60 seconds
AspNetHost.DependencyResolver.Resolve<IConfigurtionManager>().ReconnectTimeout = TimeSpan.FromSeconds(60);
Make sure You add using SignalR.Infrastructure to get the Resolve extension method (we're fixing this in the next version).
Related
Here's some context: I've set up an ActiveMQBroker (version 5.13.1) with a BrokerPlugin that takes a requestId (just a UUID for tracking requests across different servers) and OAuth token (more about the OAuth token below) in the 'username' and 'password' fields, respectively, of the org.apache.activemq.command.ConnectionInfo ... it's based on this great post. On the client/consumer side, I'm wrapping the ActiveMQConnection inside of a Spring DefaultMessageListenerContainer (version 4.2.4-RELEASE) with cacheLeve=CACHE_CONSUMER (so it also caches the Session and Consumer along with the Connection).
The snag is that the client's OAuth token expires every 20 minutes, so I've set up a ScheduledExecutorService on the client side to refresh the OAuth token every 18 minutes.
My question is that if my scheduled task on the client side calls ActiveMQConnection#cleanup() followed by ActiveMQConnection#changeUserInfo(newRequestId, newAuthToken) ... will that negatively impact spring's DefaultMessageListenerContainer that is holding onto that same ActiveMQConnection? Or another way to ask, is there a "right" way for my client code to set the new "username" and "password" fields in the ActiveMQConnection without messing anything up in the DefaultMessageListenerContainer? I'm especially concerned with any multi-threading issues since the DefaultMessageListenerContainer has several consumer threads ... and my ScheduledExecutorService is, of course, running it's own thread to update the OAuth token into the ActiveMQConnection.
Would it be enough to extend DefaultMessageListenerContainer and wrap my update of the OAuth token inside a synchronized block of the 'sharedConnectionMonitor', e.g. would something the following be necessary and/or sufficient:
public class OAuthMessageListenerContainer extends DefaultMessageListenerContainer {
public void updateOAuthToken(String requestId, String authToken) throws JMSException {
synchronized(sharedConnectionMonitor) {
ActiveMQConnection amqConn = (ActiveMQConnection)getSharedConnection();
amqConn.doCleanup(true);
amqConn.changeUserInfo(requestId, authToken);
}
}
}
FWIW here's how I solved the issue:
1) I first had to upgrade ActiveMQ from 5.13.0 to 5.13.3 because 5.13.0 had a thread deadlock issue with the FailoverTransport when trying to reconnect to the broker after the transport was interrupted, e.g. network glitch, server restart, zookeeper electing a new "master" broker (we're using replicated leveldb on the server with 3 brokers)
2) After working past the thread deadlock in ActiveMQ I realized that changing the username and password on the ActiveMQConnection basically resets the whole connection anyway ... which means the DMLC is reset ... so I made a subclass of DMLC that I could reset from a TransportListener on the connection when the transport is interrupted ... but then ran into another thread deadlock in the DMLC ... and found this answer that helped me past that.
The subclass of DMLC just needs this method:
public void restart() {
try {
shutdown();
initialize();
start();
}
catch (Exception e) {
LOG.error("Could not restart DMLC", e);
}
}
and the transport listener needs to do this:
#Override
public void transportInterrupted() {
dmlc.restart();
}
Problem description:
Lets have a service method which is called from controller:
class PaymentService {
static transactional = false
public void pay(long id) {
Member member = Member.get(id)
//long running task executing HTTP request
requestPayment(member)
}
}
The problem is if 8 users hit the same service in the same time and the time to execute the requestPayment(member) method is 30 seconds, the whole application gets stucked for 30 seconds.
The problem is even bigger than it seems, because if the HTTP request is performing well, nobody realizes any trouble. The serious problem is that availability of our web service depends on the availability of our external partner/component (in our use-case payment gateway). So when your partner starts to have performance issues, you will have them as well and even worse it will affect all parts of your app.
Evaluation:
The cause of problem is that Member.get(id) reserves a DB connection from pool and it keeps it for further use, despite requestPayment(member) method never needs to access DB. When next (9-th) request hits any other part of the application which requires DB connection (transactional service, DB select, ...) it keeps waiting (or timeouts if maxWait is set to lower duration) until the pool has an available connection, which can last even 30 seconds in our use case.
The stacktrace for the waiting thread is:
at java.lang.Object.wait(Object.java:-1)
at java.lang.Object.wait(Object.java:485)
at org.apache.commons.pool.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:1115)
at org.apache.commons.dbcp.PoolingDataSource.getConnection(PoolingDataSource.java:106)
at org.apache.commons.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044)
at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:111)
Or for timeout:
JDBC begin failed
org.apache.commons.dbcp.SQLNestedException: Cannot get a connection, pool error Timeout waiting for idle object
at org.apache.commons.dbcp.PoolingDataSource.getConnection(PoolingDataSource.java:114)
at org.apache.commons.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044)
Caused by: java.util.NoSuchElementException: Timeout waiting for idle object
at org.apache.commons.pool.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:1167)
at org.apache.commons.dbcp.PoolingDataSource.getConnection(PoolingDataSource.java:106)
... 7 more
Obviously the same issue happens with transactional service, however it makes much more sense since the connection is reserved for the transaction.
As a temporary solution its possible to increase the pool size with maxActive property on datasource, however it doesn't solve the real problem of holding an unused connection.
As a permanent solution its possible to enclose all DB operations to transactional behavior (withTransaction{..}, #Transactional), which returns the connection back to pool after commit (or to my surprise also withNewSession{..} works). But we need to be sure that the whole call chain from controller up to the requestPayment(member) method doesn't leak the connection.
I'd like to be able to throw an exception in the requestPayment(member) method if the connection is "leaked" (similar to Propagation.NEVER transactional behavior), so I can reveal the issue early during test phase.
After digging in the source code I've found the solution:
class PaymentService {
static transactional = false
def sessionFactory
public void pay(long id) {
Member member = Member.get(id)
sessionFactory.currentSession.disconnect()
//long running task executing HTTP request
requestPayment(member)
}
}
The above statement releases the connection back to pool.
If executed from transactional context, an exception is thrown (org.hibernate.HibernateException connnection proxy not usable after transaction completion), since we can't release such a connection (which is exactly what I needed).
Javadoc:
Disconnect the Session from the current JDBC connection. If the
connection was obtained by Hibernate close it and return it to the
connection pool; otherwise, return it to the application.
This is used by applications which supply JDBC connections to
Hibernate and which require long-sessions (or long-conversations)
I have modified to implement channel interceptor in spring-websocket-portfolio sample application (https://github.com/rstoyanchev/spring-websocket-portfolio). whenever the client disconnects, channel interceptor is processed twice. I have similar implementation in my production application. As it is being invoked twice so it has unwanted result for the 2nd invocation. I had put work around for the time being. But wondering why my channel interceptor is invoked twice? Any help would be highly appreciated.
modified items: WebSocketConfig.java:
#Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.setInterceptors(channelInterceptor());
}
#Bean
public ChannelInterceptor channelInterceptor() {
return new ChannelInterceptor();
}
ChannelInterceptor :
package org.springframework.samples.portfolio.config;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptorAdapter;
public class ChannelInterceptor extends ChannelInterceptorAdapter {
#Override
public void postSend(Message<?> message, MessageChannel channel, boolean sent) {
StompHeaderAccessor sha = StompHeaderAccessor.wrap(message);
System.out.println(sha.getCommand() + " " + sha);
switch (sha.getCommand()) {
case CONNECT: {
System.out.println("connected:"+sha.getSessionId());
break;
}
case DISCONNECT: {
System.out.println("disconnected:"+sha.getSessionId());
break;
}
default:
System.out.println("default:"+sha.getCommand());
break;
}
}
}
logs:
**disconnected**:9k1hvln6
**disconnected**:9k1hvln6
Disconnect events may happen more than once for the same session, your interceptor should be idempotent and ignore duplicate events.
You may also consider using application events (SessionConnectEvent, SessionDisconnectEvent...) instead of a channel interceptor. Here's an example of an idempotent event listener: https://github.com/salmar/spring-websocket-chat/blob/master/src/main/java/com/sergialmar/wschat/event/PresenceEventListener.java
Generally a DISCONNECT frame comes the client side, is processed in the StompSubProtocolHandler, and is then propagated to the broker. However, a connection can also be closed or lost without a DISCONNECT frame. Regardless of how a connection is closed, the StompSubProtocolMessageHandler generates a DISCONNECT frame. So there is some redundancy on the server side to ensure the broker is aware the client connection is gone.
As Sergi mentioned you can either subscribe to listen for SessionDisconnectEvent (of which there should be only one) and other AbstractSubProtocol events or ensure your code is idempotent.
In ASP.NET MVC3, I can't seem to override a session timeout. I have set breakpoints at all the relevant server-side code points I can think of (controller actions and methods in globax.ax.cs) but nothing seems to get hit on session timeout.
I even applied an attribute as suggested here: (http://www.tyronedavisjr.com/2008/11/23/detecting-session-timeouts-using-a-aspnet-mvc-action-filter/) but even it was not hit when the session timed out. Surely the timeout must be session-side, but where?
Does anyone know what exactly happens when an ASP.NET MVC application has a session timeout?
What sessionState mode are you using? (<sessionState mode=" ... "> in web.config)
You should be able to add the following method to your Global.asax.cs to override the default Session_End behaviour:
protected void Session_OnEnd(object sender, EventArgs e)
{
// insert code here
}
Things to bear in mind:
The Session_OnEnd / Session_End event will only be called if the HttpSessionState.Mode property value is InProc (this is the default, so if you've not changed it in the web.config this should be fine). If you've changed it to StateServer or SQLServer, then the Session_OnEnd event in the Global.asax file is ignored.
The Session_OnEnd / Session_End event is called by the application when it abandons the session - not when you close the browser. You can manually trigger it by calling Session.Abandon
Typically, session timeouts can be handled in the Session_End event in your Global.asax
void Session_End(object sender, EventArgs e) {
// perform last minute procedures before session ends
}
According to the MSDN, the HttpSessionState.Timeout property has a setter and can be changed from within your application's code as well as permanently in the web.config
I have an app that calls a WCF service in high frequency. The app starts out working fine and then after a few minutes, every call starts generating this error:
System.TimeoutException: The request channel timed out attempting to send after 00:02:00. Increase the timeout value passed to the call to Request or increase the SendTimeout value on the Binding.
I've tried everything I can think of to get around this error, such as:
Setting multiple concurrencymode on the service:
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
public class ListingService : IListingService
Setting higher limits for max concurrent calls/sessions/instances in the service web.config:
serviceThrottling maxConcurrentCalls="100000" maxConcurrentSessions="100000" maxConcurrentInstances="100000"
Setting a higher servicepointmanager defaultconnectionlimit on application_start on the service's global.asax:
protected void Application_Start(object sender, EventArgs e)
{
System.Net.ServicePointManager.DefaultConnectionLimit = 100000;
}
Making sure I'm closing client connections:
using (var client = new ListingServiceClient())
{
client.SaveListing(listing);
client.Close();
}
Here is the services web.config - http://pastebin.com/d9qtZUKN
However, I'm still getting the error. I'm sure that the service call does not take that long. Any ideas?
The issue was the db timing out which caused WCF to time out.