Token Bucket или Leaking Bucket для сообщений - PullRequest
2 голосов
/ 17 июля 2009

Я пытаюсь ограничить скорость отправки моего приложения до 900 Кбит / с, но проблема в том, что используемый мной протокол ориентирован на сообщения, а сообщения имеют очень разные размеры. Я могу иметь сообщения от 40 байт до 125000 байт, и все сообщения отправляются как атомарные единицы.

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

Это моя маленькая реализация в C:

typedef struct token_buffer {
  size_t capacity;
  size_t tokens;
  double rate;
  uint64_t timestamp;

} token_buffer;


static uint64_t time_now()
{
  struct timeval ts;
  gettimeofday(&ts, NULL);
  return (uint64_t)(ts.tv_sec * 1000 + ts.tv_usec/1000);
}

static int token_buffer_init(token_buffer *tbf, size_t max_burst, double rate)
{
  tbf->capacity = max_burst;
  tbf->tokens   = max_burst;
  tbf->rate = rate;
  tbf->timestamp = time_now();
}

static size_t token_buffer_consume(token_buffer *tbf, size_t bytes)
{
  // Update the tokens
  uint64_t now = time_now();
  size_t delta = (size_t)(tbf->rate * (now - tbf->timestamp));
  tbf->tokens = (tbf->capacity < tbf->tokens+delta)?tbf->capacity:tbf->tokens+delta;
  tbf->timestamp = now;

  fprintf(stdout, "TOKENS %d  bytes: %d\n", tbf->tokens, bytes);

  if(bytes <= tbf->tokens) {
    tbf->tokens -= bytes;
  } else {
    return -1;
  }

  return 0;
}

Тогда где-то в main ():

while(1) {
  len = read_msg(&msg, file);

  // Loop until we have enough tokens.
  // if len is larger than the bucket capacity the loop never ends.
  // if the capacity is too large then no rate limit occurs.
  while(token_buffer_consume(&tbf,msg, len) != 0) {}

  send_to_net(&msg, len);
}

1 Ответ

2 голосов
/ 18 июля 2009

Вы ограничиваете свой максимальный размер сообщения max_burst (который вначале назначается для tbf- >acity) - поскольку токены tbf-> никогда не превышают этого значения, большие сообщения никогда не будут отправляться из-за этой проверки:

if(bytes <= tbf->tokens) {
    tbf->tokens -= bytes;
  } else {
    return -1;
  }

Таким образом, код действительно устанавливает жесткое ограничение для пакета, равное max_burst - поэтому вам следует фрагментировать свои сообщения, если вы хотите этот размер пакета.

Предполагая, что это единственное место в коде, куда вы можете вставить ограничитель, вы можете получить лучший результат, если замените вышеуказанную часть на:

if(tbf->tokens > 0) {
  tbf->tokens -= bytes;
} else {
  return -1;
}

Семантика будет немного отличаться, но в среднем в течение длительного периода времени она должна приблизительно соответствовать ожидаемой вами скорости. Конечно, если вы отправляете 125 КБ в одном сообщении по каналу 1 Гбит / с, вряд ли можно говорить о скорости 900 Кбит / с - это будет полный пакет пакетов в 1 Гбит / с, и они должны быть где-то в очереди в случае, если есть каналы с более низкой скоростью - следовательно будьте готовы потерять некоторые пакеты в этом случае.

Но, в зависимости от вашего приложения и протокола транспортной сети, который вы используете (TCP / UDP / SCTP / ...?), Вы можете переместить код формирования вниз по стеку - поскольку пакеты в сети обычно являются только в любом случае, максимум 1500 байт (включая различные заголовки сетевых / транспортных протоколов)

Одной вещью, которая может быть интересна для тестирования, является http://www.linuxfoundation.org/en/Net:Netem - если ваша цель состоит в том, чтобы заняться ссылками с меньшей емкостью. Или возьмите пару старых маршрутизаторов с последовательными портами 1 Мбит / с, подключенными друг к другу.

...