Как я могу реализовать ограниченную скорость загрузки в Java?

Я собираюсь реализовать (простое) приложение-загрузчик на Java в качестве личного упражнения. Он будет выполнять несколько заданий в разных потоках, так что я буду загружать несколько файлов одновременно во время выполнения.

Я хочу быть в состоянии определить ограничение скорости загрузки, которое распределяется между всеми заданиями на загрузку, но я не знаю, как это сделать даже для одной задачи загрузки. Как мне это сделать? Какие решения я должен попробовать внедрить?

Благодарю.

4 ответа

Решение

Я бы начал с DownloadManager, который управляет всеми загрузками.

interface DownloadManager
{
    public InputStream registerDownload(InputStream stream);
}

Весь код, который хочет принять участие в управляемой полосе пропускания, зарегистрирует свой поток в менеджере загрузок, прежде чем начнет читать из него. В своем методе registerDownload() менеджер упаковывает данный поток ввода в ManagedBandwidthStream,

   public class ManagedBandwidthStream extends InputStream
   {
      private DownloadManagerImpl owner;

      public ManagedBandwidthStream(
            InputStream original,
            DownloadManagerImpl owner
         )
      {
         super(original);
         this.owner = owner;
      }

      public int read(byte[] b, int offset, int length)
      {
          owner.read(this, b, offset, length);
      }

      // used by DownloadManager to actually read from the stream
      int actuallyRead(byte[] b, int offset, int length)
      {
          super.read(b, offset, length);
      }

      // also override other read() methods to delegate to the read() above
   }

Поток гарантирует, что все вызовы read() будут направлены обратно в менеджер загрузок.

class DownloadManagerImpl implements DownloadManager
{
   public InputStream registerDownload(InputStream in)
   {
       return new ManagedDownloadStream(in);
   }

   void read(ManagedDownloadStream source, byte[] b, int offset, int len)
   {
      // all your streams now call this method.
      // You can decide how much data to actually read.
      int allowed = getAllowedDataRead(source, len);
      int read = source.actuallyRead(b, offset, len);
      recordBytesRead(read);  // update counters for number of bytes read
   }
}

Тогда ваша стратегия распределения пропускной способности заключается в том, как вы реализуете getAllowedDataRead().

Простой способ регулирования полосы пропускания состоит в том, чтобы сохранить счетчик того, сколько еще байтов может быть прочитано за данный период (например, 1 секунда). Каждый вызов read проверяет счетчик и использует его для ограничения фактического количества прочитанных байтов. Таймер используется для сброса счетчика.

На практике распределение полосы пропускания между несколькими потоками может быть довольно сложным, особенно во избежание голода и содействия справедливости, но это должно дать вам справедливое начало.

  1. Решите, какую полосу пропускания вы хотите использовать, в байтах / секунду.
  2. Установите задержку сетевого пути к цели в секундах.
  3. Умножьте, чтобы получить ответ в байтах (байт / секунда * секунд = байт).
  4. Разделите на количество одновременных соединений.
  5. Установите буфер приема сокета каждого соединения на этот номер.

Этот вопрос имеет высокий уровень, поэтому я надеюсь, что вы не ожидаете ответа низкого уровня. В общем, вам сначала нужно определить / решить, какие сетевые утилиты вы будете использовать. Например, вы собираетесь открыть стандартный сокет Java? Есть ли сторонняя сетевая библиотека, которую вы будете использовать? Вы даже познакомились с любым из доступных вариантов?

В самом общем смысле вы можете управлять пропускной способностью через сетевую библиотеку, которую вы выбираете. Это должна быть относительно простая формула.

У вас будет какой-то объект (назовите его сокетом), для которого вы устанавливаете ограничение пропускной способности. Вы будете устанавливать ограничение пропускной способности на своих сокетах (в общем), чтобы быть общей пропускной способностью / числом активных соединений. Вы можете оптимизировать это число на постоянной основе, если некоторые соединения не используют свое полное распределение пропускной способности. Обратитесь за помощью по этому алгоритму, когда вы туда доберетесь, и если вам даже все равно...

Вторая часть уравнения будет такой: может ли библиотека ОС / сети уже контролировать пропускную способность для вас, просто задав ей число ограничения скорости, или вам нужно самостоятельно контролировать этот процесс, ограничивая скорости чтения / записи? Это не так просто, как может показаться, поскольку ОС может иметь буферы сокетов TCP, которые будут считывать данные до полного заполнения. Предположим, у вас был буфер сокета 2 Мб для входящего трафика. Если вы полагались, что удаленная сторона прекращает отправку данных только при заполнении буфера 2 МБ, вам придется подождать 2 МБ данных для передачи, прежде чем вы сможете ограничить скорость, удалив из очереди, у вас всегда будет огромный всплеск на каждом сокете, прежде чем вы сможете оценить ограничение.

В этот момент вы начинаете говорить о написании протокола, который будет работать по протоколу TCP (или UDP), чтобы одна сторона могла сказать другой стороне: "Хорошо, отправьте больше данных" или "Подождите, мой предел пропускной способности был временно достигнут". Короче говоря, начните, затем задавайте вопросы, как только у вас есть реализация и вы хотите ее улучшить...

  1. Отправить / получить данные
  2. Спать
  3. Повторение

Вот в основном, как работает большинство лимитеров (так же, как wget)

Другие вопросы по тегам