Токеновое ведро или протекающее ведро для сообщений

StackOverflow https://stackoverflow.com/questions/1145337

  •  16-09-2019
  •  | 
  •  

Вопрос

Я пытаюсь ограничить скорость отправки приложения 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);
}
Это было полезно?

Решение

Вы ограничиваете максимальный размер сообщения MAX_BURST (который вначале назначается 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 Кбит / с. Будьте готовы потерять некоторые пакеты в этом случае.

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top