All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.grinder.plugin.http.SlowClientBandwidthLimiterFactory Maven / Gradle / Ivy

There is a newer version: 4.0.0
Show newest version
// 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