我正在编写一种接收消息的算法,然后确定它们是否符合既定的消息传递速率。
例如,在任何50秒窗口中发送的消息不得超过5条。因此,这个窗口必须是一个滚动窗口。
我已经从this post.实现了这个令牌桶算法但是,我不能让它始终如一地工作。它传递了一些测试用例而不是其他测试用例,这让我觉得这里隐藏着一个逻辑问题。
这是我到目前为止所做的:
public class Messaging {
//times in millis
double time_before;
double time_now;
double now;
double time_passed;
double allowance;
//constants
static double per = 50000; // 50 seconds
static double rate = 5; //5 messages
public Messaging(){
time_before = System.currentTimeMillis();
allowance = rate;
}
public void onEvent(){
time_now = System.currentTimeMillis();
time_passed = time_now - time_before;
time_before = time_now;
allowance += time_passed * (rate / per);
if (allowance > rate){
allowance = rate;
System.out.println("Reset Allowance");
}
if (allowance < 1.0){
System.out.println("Discard");
}else{
System.out.println("Forward message");
allowance -= 1.0;
}
}
虽然这不起作用!
public static void main(String[] args) {
Messaging orders = new Messaging();
for (int i = 0; i < 10; i++) {
orders.onEvent();
try {
Thread.sleep(5000);
} catch (Exception ex) {
}
}
}
运行上面的代码可以得到:
Forward message. Time: 1469830426910
Forward message. Time: 1469830431912
Forward message. Time: 1469830436913
Forward message. Time: 1469830441920
Forward message. Time: 1469830446929
Forward message. Time: 1469830451937
Forward message. Time: 1469830456939
Forward message. Time: 1469830461952
Forward message. Time: 1469830466962
Discard. Time: 1469830471970
Total time passed: 50067
为什么只丢弃最后一条消息?不应该在第5条消息之后自动失败吗?
我想帮助这个特定的实现。实际的实现将使用没有队列等的专有语言。
答案 0 :(得分:0)
对于速率受限的滑动窗口,您需要将每条消息及其时间戳排队。这样,当队列已满时,您只需丢弃任何新消息。当队列末尾的消息在那里的时间超过规定的时间时,他们就会离开队列,你就有了更多新消息的空间。
class MessageBuffer {
class Message {
double timestamp;
String value; // Can be any type you need it to be
public Message(double timestamp, String value) {
this.timestamp = timestamp;
this.value = value;
}
}
static final double WINDOW_SIZE = 5;
static final double TIME_LIMIT = 50000;
Queue<Message> messages = new ArrayDeque<>(WINDOW_SIZE);
public void onEvent(String message) {
double now = System.currentTimeMillis();
// If the queue has messages in them that are no longer in the sliding window,
// remove them from the queue
while (messages.size() > 0
&& messages.peek().timestamp + TIME_LIMIT > now)
messages.remove();
// If there is room in the queue, process this message, otherwise discard it
if (messages.size() < WINDOW_SIZE) {
System.out.println("Forward message: " + message);
messages.add(new Message(now, message));
} else {
System.out.println("Discard message: " + message);
}
}
}
如果没有此时间戳信息,您无法告知消息何时离开滑动窗口,因此您无法知道您的窗口是否已满。仅供参考,您链接的示例是近似值,实际上可以在50秒内将您限制为少于5条消息。
两个小挑选:
now
不应该是成员变量 - 当您第一次分配它时, 表示当前时间,但是一旦方法完成,并且调用另一个方法,该值是陈旧的 - 它实际上并不代表当前时间。答案 1 :(得分:0)
您使用的算法是近似值 - 它会平均为您提供您正在寻找的费率。在original post中,作者声称:
&#39;津贴&#39;最多以每秒5/8单位的速度增长,即每8秒最多5个单位。转发的每条消息都会扣除一个单位,因此每八秒钟就不能发送五封以上的消息。
这不完全正确 - 如果津贴以它的最大值开始,比如5,它以每秒5/8个单位增长,然后对于每个发送的消息,减去1。所以津贴以每秒3/8个单位的速度缩小,从5开始,我们可以在节流发生之前发送大约10个消息。
如果您的消息没有以节流率的速度进入,那么它会增加限额。然后,当消息加快时,你会有一段短暂的时间,你可能会在限制开始之前处理2 * rate
消息。如果你改变你的循环来进行20次迭代,而不是10次,你就可以了。我会看到最终节流的确表现得像你期望的那样。或者,如果您将allowance设置为0,而不是rate,则会立即限制。