net.grinder.plugin.http.SlowClientBandwidthLimiterFactory Maven / Gradle / Ivy
// Copyright (C) 2006 - 2008 Philip Aston
// All rights reserved.
//
// This file is part of The Grinder software distribution. Refer to
// the file LICENSE which is part of The Grinder distribution for
// licensing details. The Grinder distribution is available on the
// Internet at http://grinder.sourceforge.net/
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
package net.grinder.plugin.http;
import net.grinder.util.Sleeper;
import net.grinder.util.Sleeper.ShutdownException;
import HTTPClient.HTTPConnection.BandwidthLimiter;
import HTTPClient.HTTPConnection.BandwidthLimiterFactory;
/**
* BandwidthLimiterFactory that creates a
* {@link HTTPClient.HTTPConnection.BandwidthLimiter}
* that restricts the bandwidth of the data transfered through the buffer in
* {@link HTTPClient.HTTPConnection.BandwidthLimiter#maximumBytes} by sleeping.
*
* @author Philip Aston
*/
final class SlowClientBandwidthLimiterFactory
implements BandwidthLimiterFactory {
private final Sleeper m_sleeper;
private final int m_targetBPS;
private final int m_bufferIncrement;
public SlowClientBandwidthLimiterFactory(Sleeper sleeper, int targetBPS) {
m_sleeper = sleeper;
m_targetBPS = targetBPS;
// We must use a larger buffer increment for higher BPS rates due to the
// precision to which we can sleep (maybe ~10ms). Limiting for higher
// BPS rates will only apply to large messages, but that's not something
// we can help.
//
// I considered adjusting the buffer increment based on the target baud, or
// dynamically based on the measured performance. I discounted this because
// there's no obvious algorithm, and its likely to cause non-linear
// behaviour due to external influences such as the MTU size. Also, having
// the increment too small will increase the work that we have to do within
// The Grinder, which might significantly skew timings.
m_bufferIncrement = Math.max(100, m_targetBPS / 500);
}
public BandwidthLimiter create() {
return new SlowClientBandwidthLimiter();
}
private final class SlowClientBandwidthLimiter implements BandwidthLimiter {
// Too large and the instantaneous bandwidth varies too much.
// Too small and we don't reach the target fast enough.
private static final float DAMPING_FACTOR = 0.5f;
private long m_startTime;
private int m_sleepTime;
private float m_damping;
public int maximumBytes(int position) {
final long now = m_sleeper.getTimeInMilliseconds();
if (position == 0) {
m_startTime = now;
// Set the initial sleep time to 0 so we start pumping bytes straight
// away.
m_sleepTime = 0;
// Set the second sleep time based on the first lot of bytes transfered.
// The damping is 2 to account for the initial call.
m_damping = 2;
}
else {
final long expectedTime = (long)position * 8 * 1000 / m_targetBPS;
final long actualTime = now - m_startTime;
m_sleepTime += (expectedTime - actualTime) * m_damping;
if (m_sleepTime < 0) {
m_sleepTime = 0;
}
m_damping = DAMPING_FACTOR;
}
try {
m_sleeper.sleepNormal(m_sleepTime, 0);
}
catch (ShutdownException e) {
// Don't propagate exception - the thread will work out it's shutdown
// soon enough.
}
// Allow m_bufferIncrement bytes to be read.
return m_bufferIncrement;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy