com.crankuptheamps.client.ExponentialDelayStrategy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of amps-client Show documentation
Show all versions of amps-client Show documentation
AMPS Java client by 60East Technologies, Inc.
////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2010-2022 60East Technologies Inc., All Rights Reserved.
//
// This computer software is owned by 60East Technologies Inc. and is
// protected by U.S. copyright laws and other laws and by international
// treaties. This computer software is furnished by 60East Technologies
// Inc. pursuant to a written license agreement and may be used, copied,
// transmitted, and stored only in accordance with the terms of such
// license agreement and with the inclusion of the above copyright notice.
// This computer software or any other copies thereof may not be provided
// or otherwise made available to any other person.
//
// U.S. Government Restricted Rights. This computer software: (a) was
// developed at private expense and is in all respects the proprietary
// information of 60East Technologies Inc.; (b) was not developed with
// government funds; (c) is a trade secret of 60East Technologies Inc.
// for all purposes of the Freedom of Information Act; and (d) is a
// commercial item and thus, pursuant to Section 12.212 of the Federal
// Acquisition Regulations (FAR) and DFAR Supplement Section 227.7202,
// Government's use, duplication or disclosure of the computer software
// is subject to the restrictions set forth by 60East Technologies Inc..
//
////////////////////////////////////////////////////////////////////////////
package com.crankuptheamps.client;
import com.crankuptheamps.client.exception.*;
/**
* ExponentialDelayStrategy is an implementation that exponentially
* "backs off" when reconnecting to the same server, with a maximum
* number of retries before it gives up entirely.
*/
public class ExponentialDelayStrategy implements ReconnectDelayStrategy
{
/**
* A specialized exception type thrown by ExponentialDelayStrategy
* to indicate that the client should "give up" on attempting to
* reconnect to a server.
*/
public static class MaximumRetryExceeded extends AMPSException
{
private static final long serialVersionUID = 1L;
}
/**
* The default time (in milliseconds) to wait before reconnecting to a
* server for the first time after a failed connection.
*/
public static final int DEFAULT_DELAY = 200;
/**
* The default maximum time to wait between any reconnection attempts.
* Exponential backoff will not exceed this maximum.
*/
public static final int DEFAULT_MAXIMUM_DELAY = 20 * 1000;
/**
* The default exponent to use for calculating the next delay time. For
* example if the initial time is 200ms and the exponent is 2.0, the next
* delay will be 400ms, then 800ms, etc.
*/
public static final double DEFAULT_BACKOFF_EXPONENT = 2.0;
/**
* The default time (in millseconds) to allow reconnect attempts to
* continue without a successful connection, before "giving up" and
* abandoning the connection attempt. 0 means never give up.
*/
public static final int DEFAULT_MAXIMUM_RETRY_TIME = 0;
/**
* The amount of 'jitter' to apply when calculating a delay time,
* measured in multiples of the initial delay. Jitter is used to reduce
* the number of simultaneous reconnects that may be issued from multiple
* clients.
*/
public static final double DEFAULT_JITTER = 1.0;
/**
* Constructs an ExponentialDelayStrategy, the default strategy for
* HAClient, using the default values for the class.
*
* For example, when constructed, the new object will use the
* {@link #DEFAULT_DELAY} ({@value #DEFAULT_DELAY} ms)
* as the initial delay, the {@link #DEFAULT_BACKOFF_EXPONENT} ({@value #DEFAULT_BACKOFF_EXPONENT})
* as the backoff exponent, the {@link #DEFAULT_MAXIMUM_RETRY_TIME}
* ({@value #DEFAULT_MAXIMUM_RETRY_TIME} -- never time out) as the maximum
* retry time, and so on. You can use the setters provided on the class to
* adjust these settings.
*/
public ExponentialDelayStrategy()
{
_initialDelay = DEFAULT_DELAY;
_maximumDelay = DEFAULT_MAXIMUM_DELAY;
_backoffExponent = DEFAULT_BACKOFF_EXPONENT;
_maximumRetryTime = DEFAULT_MAXIMUM_RETRY_TIME;
_jitter = DEFAULT_JITTER;
_currentDelay = _initialDelay;
_startTime = 0;
}
/**
* Sets the time (in milliseconds) to wait before reconnecting to
* a server for the first time after a failed connection.
* @param initialDelay_ The new initial delay value, in milliseconds.
* @return Self.
*/
public ExponentialDelayStrategy setInitialDelay(int initialDelay_)
{
_initialDelay = initialDelay_;
_currentDelay = initialDelay_;
return this;
}
/**
* Sets the maximum time to wait between any reconnection attempts.
* Exponential backoff will not exceed this maximum.
* @param maximumDelay_ The new maximum delay value, in milliseconds.
* @return Self.
*/
public ExponentialDelayStrategy setMaximumDelay(int maximumDelay_)
{
_maximumDelay = maximumDelay_;
return this;
}
/**
* Sets the exponent to use for calculating the next delay time. For
* example if the initial time is 200ms and the exponent is 2.0, the next
* delay will be 400ms, then 800ms, etc.
* @param exponent_ The new exponent value.
* @return Self.
*/
public ExponentialDelayStrategy setBackoffExponent(double exponent_)
{
_backoffExponent = exponent_;
return this;
}
/**
* Sets the time (in millseconds) to allow reconnect attempts to
* continue without a successful connection, before "giving up" and
* abandoning the connection attempt. 0 means never give up.
* @param maximumRetryTime_ The new maximum retry time, in milliseconds.
* @return Self.
*/
public ExponentialDelayStrategy setMaximumRetryTime(
int maximumRetryTime_)
{
_maximumRetryTime = maximumRetryTime_;
return this;
}
/**
* Sets the jitter factor used to add randomness to the delay time.
* Jitter is represented as a multiple of the initial delay time;
* a random number from [0, (JITTER * INITIAL_DELAY) ) is added
* to nonzero time delays.
*
* @param jitter_ The jitter factor, in multiples of the initial delay.
* @return self.
*/
public ExponentialDelayStrategy setJitter(double jitter_)
{
_jitter = jitter_;
return this;
}
public int getConnectWaitDuration(String uri_) throws Exception
{
_throwIfMaximumExceeded();
if(_lastUri == null || !_lastUri.equals(uri_))
{
_lastUri = uri_;
if(_firstUri != null && _firstUri.equals(uri_))
{
// we've wrapped around the "list." Delay.
return _currentDurationAndIncrease();
}
else if(_firstUri == null)
{
_firstUri = uri_;
}
return 0;
}
return _currentDurationAndIncrease();
}
public void reset()
{
_lastUri = null;
_firstUri = null;
_currentDelay = _initialDelay;
_startTime = 0;
}
/**
* An internal function used to check if the configured retry has been
* exceeded, and if so, throw MaximumRetryExceeded to cause the calling
* HAClient to give up on reconnecting to a server.
*
* @throws MaximumRetryExceeded The configured maximum retry time has
* been exceeded.
*/
protected void _throwIfMaximumExceeded() throws MaximumRetryExceeded
{
if(_maximumRetryTime > 0)
{
long now = System.currentTimeMillis();
if(_startTime > 0)
{
if(now - _startTime > _maximumRetryTime)
{
throw new MaximumRetryExceeded();
}
}
else
{
_startTime = now;
}
}
}
/**
* Returns the _currentDelay, and also expands _currentDelay based
* on the configured exponent and maximum.
* @return The delay that should be used, prior to increasing it.
* @throws MaximumRetryExceeded The configured maximum retry time has
* been exceeded.
*/
protected int _currentDurationAndIncrease() throws MaximumRetryExceeded
{
int newDelay = (int) (_currentDelay * _backoffExponent);
if(newDelay > _maximumDelay) newDelay = _maximumDelay;
// Don't exceed _maximumDelay ...
int maxJitter = (int) (_jitter * _initialDelay);
// but don't go negative due to large jitter
if (maxJitter > _maximumDelay) maxJitter = _maximumDelay;
int currentDelay = _currentDelay;
if (_jitter > 0.0)
{
// Don't exceed _maximumDelay, could happen with large jitter
if(currentDelay > _maximumDelay - maxJitter)
currentDelay = (_maximumDelay - maxJitter > _initialDelay) ?
_maximumDelay - maxJitter : _initialDelay;
currentDelay += (int) (Math.random() * _jitter * _initialDelay);
if (currentDelay > _maximumDelay) currentDelay = _maximumDelay;
}
if(_maximumRetryTime > 0)
{
int timeElapsed = (int) (System.currentTimeMillis() - _startTime);
if(timeElapsed + currentDelay > _maximumRetryTime)
{
if(timeElapsed > _maximumRetryTime)
throw new MaximumRetryExceeded();
currentDelay = _maximumRetryTime - timeElapsed;
}
}
_currentDelay = newDelay;
return currentDelay;
}
protected int _initialDelay;
protected int _maximumDelay;
protected double _backoffExponent;
protected int _maximumRetryTime;
protected double _jitter;
protected int _currentDelay;
protected String _lastUri;
protected String _firstUri;
protected long _startTime;
}