Token Bucket或Leaking Bucket用于消息

时间:2009-07-17 19:52:20

标签: limit token rate bucket

我试图将我的应用程序发送速率限制为900kbps,但问题是我使用的协议是面向消息的,并且消息具有非常不同的大小。我可以将消息从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 个答案:

答案 0 :(得分:2)

您通过max_burst限制最大邮件大小(在开始时将其分配给tbf->容量) - 因为tbf-&gt;令牌永远不会增加超过该值,因此检查不会发送更大的邮件:

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

因此,代码确实将爆发的硬限制设置为max_burst - 所以如果你想要这个突发大小,你应该分段你的消息。

假设这是代码中唯一可以插入限制器的地方,如果用以下内容替换上面的部分,可能会得到更好的结果:

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

语义会略有不同,但平均而言,在很长一段时间内,它应该能够达到你想要的速度。当然,如果您通过1gbps链路在一条消息中发送125K,那么很难谈论900kbps速率 - 它将是完整的1gbps突发数据包,并且如果存在低速链路,它们将需要在某处排队 - 因此准备在这种情况下丢失一些数据包。

但是,根据您的应用程序和您正在使用的传输网络协议(TCP / UDP / SCTP / ...?),您可能希望将整形代码向下移动 - 因为网络上的数据包通常只是最多1500字节(包括各种网络/传输协议头)

测试可能有趣的一件事是http://www.linuxfoundation.org/en/Net:Netem - 如果你的目标是试图解决容量较小的链接问题。或者,抓住一些旧的路由器,其中1mbps串口连接在一起。