This closes #657 ARTEMIS-650 clarify doc, use TTL default for 0 heartbeat

This commit is contained in:
Andy Taylor 2016-07-25 16:27:30 +01:00
commit d7c149b416
4 changed files with 65 additions and 57 deletions

View File

@ -95,7 +95,7 @@ public class StompFrameHandlerV11 extends VersionedStompFrameHandler implements
response.addHeader(Stomp.Headers.Connected.HEART_BEAT, "0,0"); response.addHeader(Stomp.Headers.Connected.HEART_BEAT, "0,0");
} }
else { else {
response.addHeader(Stomp.Headers.Connected.HEART_BEAT, Long.toString(heartBeater.serverPingPeriod) + "," + Long.toString(heartBeater.clientPingResponse)); response.addHeader(Stomp.Headers.Connected.HEART_BEAT, heartBeater.serverPingPeriod + "," + heartBeater.clientPingResponse);
} }
} }
} }
@ -116,8 +116,6 @@ public class StompFrameHandlerV11 extends VersionedStompFrameHandler implements
return response; return response;
} }
//ping parameters, hard-code for now
//the server can support min 20 milliseconds and receive ping at 100 milliseconds (20,100)
private void handleHeartBeat(String heartBeatHeader) throws ActiveMQStompException { private void handleHeartBeat(String heartBeatHeader) throws ActiveMQStompException {
String[] params = heartBeatHeader.split(","); String[] params = heartBeatHeader.split(",");
if (params.length != 2) { if (params.length != 2) {
@ -129,10 +127,8 @@ public class StompFrameHandlerV11 extends VersionedStompFrameHandler implements
//client receive ping //client receive ping
long minAcceptInterval = Long.valueOf(params[1]); long minAcceptInterval = Long.valueOf(params[1]);
if ((minPingInterval != 0) || (minAcceptInterval != 0)) {
heartBeater = new HeartBeater(minPingInterval, minAcceptInterval); heartBeater = new HeartBeater(minPingInterval, minAcceptInterval);
} }
}
@Override @Override
public StompFrame onDisconnect(StompFrame frame) { public StompFrame onDisconnect(StompFrame frame) {
@ -266,18 +262,27 @@ public class StompFrameHandlerV11 extends VersionedStompFrameHandler implements
private HeartBeater(final long clientPing, final long clientAcceptPing) { private HeartBeater(final long clientPing, final long clientAcceptPing) {
connectionEntry = ((RemotingServiceImpl)connection.getManager().getServer().getRemotingService()).getConnectionEntry(connection.getID()); connectionEntry = ((RemotingServiceImpl)connection.getManager().getServer().getRemotingService()).getConnectionEntry(connection.getID());
clientPingResponse = clientPing;
if (connectionEntry != null) {
String heartBeatToTtlModifierStr = (String) connection.getAcceptorUsed().getConfiguration().get(TransportConstants.HEART_BEAT_TO_CONNECTION_TTL_MODIFIER);
double heartBeatToTtlModifier = heartBeatToTtlModifierStr == null ? 2 : Double.valueOf(heartBeatToTtlModifierStr);
// the default response to the client
clientPingResponse = (long) (connectionEntry.ttl / heartBeatToTtlModifier);
if (clientPing != 0) {
clientPingResponse = clientPing;
String ttlMaxStr = (String) connection.getAcceptorUsed().getConfiguration().get(TransportConstants.CONNECTION_TTL_MAX); String ttlMaxStr = (String) connection.getAcceptorUsed().getConfiguration().get(TransportConstants.CONNECTION_TTL_MAX);
long ttlMax = ttlMaxStr == null ? Long.MAX_VALUE : Long.valueOf(ttlMaxStr); long ttlMax = ttlMaxStr == null ? Long.MAX_VALUE : Long.valueOf(ttlMaxStr);
String ttlMinStr = (String) connection.getAcceptorUsed().getConfiguration().get(TransportConstants.CONNECTION_TTL_MIN); String ttlMinStr = (String) connection.getAcceptorUsed().getConfiguration().get(TransportConstants.CONNECTION_TTL_MIN);
long ttlMin = ttlMinStr == null ? 1000 : Long.valueOf(ttlMinStr); long ttlMin = ttlMinStr == null ? 1000 : Long.valueOf(ttlMinStr);
String heartBeatToTtlModifierStr = (String) connection.getAcceptorUsed().getConfiguration().get(TransportConstants.HEART_BEAT_TO_CONNECTION_TTL_MODIFIER); /* The connection's TTL should be one of the following:
double heartBeatToTtlModifier = heartBeatToTtlModifierStr == null ? 2 : Double.valueOf(heartBeatToTtlModifierStr); * 1) clientPing * heartBeatToTtlModifier
* 2) ttlMin
// The connection's TTL should be clientPing * 2, MIN_CLIENT_PING, or ttlMax set on the acceptor * 3) ttlMax
*/
long connectionTtl = (long) (clientPing * heartBeatToTtlModifier); long connectionTtl = (long) (clientPing * heartBeatToTtlModifier);
if (connectionTtl < ttlMin) { if (connectionTtl < ttlMin) {
connectionTtl = ttlMin; connectionTtl = ttlMin;
@ -291,6 +296,8 @@ public class StompFrameHandlerV11 extends VersionedStompFrameHandler implements
ActiveMQServerLogger.LOGGER.debug("Setting STOMP client TTL to: " + connectionTtl); ActiveMQServerLogger.LOGGER.debug("Setting STOMP client TTL to: " + connectionTtl);
} }
connectionEntry.ttl = connectionTtl; connectionEntry.ttl = connectionTtl;
}
}
if (clientAcceptPing != 0) { if (clientAcceptPing != 0) {
serverPingPeriod = clientAcceptPing > MIN_SERVER_PING ? clientAcceptPing : MIN_SERVER_PING; serverPingPeriod = clientAcceptPing > MIN_SERVER_PING ? clientAcceptPing : MIN_SERVER_PING;

View File

@ -271,13 +271,6 @@ subscribes (or unsubscribes) for a destination (using a `SUBSCRIBE` or
### STOMP heart-beating and connection-ttl ### STOMP heart-beating and connection-ttl
Apache ActiveMQ Artemis specifies a minimum value for both client and server heart-beat
intervals. The minimum interval for both client and server heartbeats is
500 milliseconds. That means if a client sends a CONNECT frame with
heartbeat values lower than 500, the server will defaults the value to
500 milliseconds regardless the values of the 'heart-beat' header in the
frame.
Well behaved STOMP clients will always send a DISCONNECT frame before Well behaved STOMP clients will always send a DISCONNECT frame before
closing their connections. In this case the server will clear up any closing their connections. In this case the server will clear up any
server side resources such as sessions and consumers synchronously. server side resources such as sessions and consumers synchronously.
@ -295,28 +288,33 @@ For example:
<acceptor name="stomp-acceptor">tcp://localhost:61613?protocols=STOMP;connectionTtl=20000</acceptor> <acceptor name="stomp-acceptor">tcp://localhost:61613?protocols=STOMP;connectionTtl=20000</acceptor>
The above configuration will make sure that any stomp connection that is The above configuration will make sure that any Stomp connection that is
created from that acceptor will have its connection-ttl set to 20 created from that acceptor and does not include a `heart-beat` header
seconds. The `connectionTtl` set on an acceptor will take precedence over or disables client-to-server heart-beats by specifying a `0` value will
`connection-ttl-override`. have its connection-ttl set to 20 seconds. The `connectionTtl` set on an
acceptor will take precedence over `connection-ttl-override`. The default
`connectionTtl` is 60,000 milliseconds.
Since Stomp 1.0 doesn't support heart-beating then all connections from Since Stomp 1.0 does not support heart-beating then all connections from
Stomp 1.0 clients will have a connection TTL imposed upon them by the broker Stomp 1.0 clients will have a connection TTL imposed upon them by the broker
based on the aforementioned configuration options. Likewise, any Stomp 1.1 based on the aforementioned configuration options. Likewise, any Stomp 1.1
or 1.2 clients that don't specify a heart-beat or disable heart-beating or 1.2 clients that don't specify a `heart-beat` header or disable client-to-server
(e.g. by sending `0,0` in the `heart-beat` header) will have a connection heart-beating (e.g. by sending `0,X` in the `heart-beat` header) will have
TTL imposed upon them by the broker. a connection TTL imposed upon them by the broker.
For Stomp 1.1 and 1.2 clients which send a valid `heart-beat` header then For Stomp 1.1 and 1.2 clients which send a non-zero client-to-server `heart-beat`
their connection TTL will be set accordingly. However, the broker will not header value then their connection TTL will be set accordingly. However, the broker
set the connection TTL to the same value as the specified in the `heart-beat` will not strictly set the connection TTL to the same value as the specified in the
since even small network delays could then cause spurious disconnects. Instead, `heart-beat` since even small network delays could then cause spurious disconnects.
the value in the heart-beat will be multiplied by the `heartBeatConnectionTtlModifer` Instead, the client-to-server value in the `heart-beat` will be multiplied by the
specified on the acceptor. The `heartBeatConnectionTtlModifer` is a decimal `heartBeatConnectionTtlModifer` specified on the acceptor. The
value that defaults to 2.0 so for example, if a client sends a `heart-beat` `heartBeatConnectionTtlModifer` is a decimal value that defaults to `2.0` so for
frame of `1000,0` the the connection TTL will be set to `2000` so that the example, if a client sends a `heart-beat` header of `1000,0` the the connection TTL
ping frames sent every 1000 milliseconds will have a sufficient cushion so as will be set to `2000` so that the data or ping frames sent every 1000 milliseconds will
not to be considered late and trigger a disconnect. have a sufficient cushion so as not to be considered late and trigger a disconnect.
This is also in accordance with the Stomp 1.1 and 1.2 specifications which both state,
"because of timing inaccuracies, the receiver SHOULD be tolerant and take into account
an error margin."
The minimum and maximum connection TTL allowed can also be specified on the The minimum and maximum connection TTL allowed can also be specified on the
acceptor via the `connectionTtlMin` and `connectionTtlMax` properties respectively. acceptor via the `connectionTtlMin` and `connectionTtlMax` properties respectively.
@ -329,12 +327,15 @@ of `2.0` then the connection TTL would be `40000` (i.e. `20000` * `2.0`) which w
exceed the `connectionTtlMax`. In this case the server would respond to the client exceed the `connectionTtlMax`. In this case the server would respond to the client
with a `heart-beat` header of `0,15000` (i.e. `30000` / `2.0`). As described with a `heart-beat` header of `0,15000` (i.e. `30000` / `2.0`). As described
previously, this is to make sure there is a sufficient cushion for the client previously, this is to make sure there is a sufficient cushion for the client
heart-beats. The same kind of calculation is done for `connectionTtlMin`. heart-beats in accordance with the Stomp 1.1 and 1.2 specifications. The same kind
of calculation is done for `connectionTtlMin`.
The minimum server-to-client heart-beat value is 500ms.
> **Note** > **Note**
> >
> Please note that the STOMP protocol version 1.0 does not contain any > Please note that the STOMP protocol version 1.0 does not contain any
> heartbeat frame. It is therefore the user's responsibility to make > heart-beat frame. It is therefore the user's responsibility to make
> sure data is sent within connection-ttl or the server will assume the > sure data is sent within connection-ttl or the server will assume the
> client is dead and clean up server side resources. With `Stomp 1.1` > client is dead and clean up server side resources. With `Stomp 1.1`
> users can use heart-beats to maintain the life cycle of stomp > users can use heart-beats to maintain the life cycle of stomp

View File

@ -407,7 +407,7 @@ public class StompV11Test extends StompV11TestBase {
connV11.disconnect(); connV11.disconnect();
//no heart beat for (0,0) //default heart beat for (0,0) which is default connection TTL (60000) / default heartBeatToTtlModifier (2.0) = 30000
connV11 = StompClientConnectionFactory.createClientConnection("1.1", hostname, port); connV11 = StompClientConnectionFactory.createClientConnection("1.1", hostname, port);
frame = connV11.createFrame("CONNECT"); frame = connV11.createFrame("CONNECT");
frame.addHeader("host", "127.0.0.1"); frame.addHeader("host", "127.0.0.1");
@ -420,7 +420,7 @@ public class StompV11Test extends StompV11TestBase {
assertEquals("CONNECTED", reply.getCommand()); assertEquals("CONNECTED", reply.getCommand());
assertEquals("0,0", reply.getHeader("heart-beat")); assertEquals("0,30000", reply.getHeader("heart-beat"));
Thread.sleep(5000); Thread.sleep(5000);
@ -790,7 +790,7 @@ public class StompV11Test extends StompV11TestBase {
assertEquals("CONNECTED", reply.getCommand()); assertEquals("CONNECTED", reply.getCommand());
assertEquals("0,0", reply.getHeader("heart-beat")); assertEquals("0,500", reply.getHeader("heart-beat"));
Thread.sleep(3000); Thread.sleep(3000);

View File

@ -596,7 +596,7 @@ public class StompV12Test extends StompV11TestBase {
Assert.assertEquals("CONNECTED", reply.getCommand()); Assert.assertEquals("CONNECTED", reply.getCommand());
Assert.assertEquals("0,0", reply.getHeader("heart-beat")); Assert.assertEquals("0,30000", reply.getHeader("heart-beat"));
Thread.sleep(5000); Thread.sleep(5000);