Как я могу реализовать ограниченную скорость загрузки в Java? - PullRequest
3 голосов
/ 04 мая 2010

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

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

Спасибо.

Ответы [ 4 ]

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

Я бы начал с 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 проверяет счетчик и использует его для ограничения фактического количества прочитанных байтов. Таймер используется для сброса счетчика.

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

2 голосов
/ 04 мая 2010

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

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

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

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

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

1 голос
/ 04 мая 2010
  1. Отправка / получение данных
  2. Sleep
  3. Повторите

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

...