Một Token Xô khá đơn giản để thực hiện.
Bắt đầu với một cái xô có 5 mã thông báo.
Cứ sau 5/8 giây: Nếu thùng có ít hơn 5 mã thông báo, hãy thêm một thùng.
Mỗi lần bạn muốn gửi tin nhắn: Nếu nhóm có to1 mã thông báo, hãy lấy một mã thông báo ra và gửi tin nhắn. Nếu không, chờ / thả tin nhắn / bất cứ điều gì.
(rõ ràng, trong mã thực tế, bạn sẽ sử dụng bộ đếm số nguyên thay vì mã thông báo thực và bạn có thể tối ưu hóa từng bước 5/8 bằng cách lưu trữ dấu thời gian)
Đọc lại câu hỏi, nếu giới hạn tốc độ được đặt lại hoàn toàn sau mỗi 8 giây, thì đây là một sửa đổi:
Bắt đầu với dấu thời gian, last_send
tại một thời điểm cách đây rất lâu (ví dụ: ở thời đại). Ngoài ra, hãy bắt đầu với nhóm 5 mã thông báo tương tự.
Tấn công cứ sau 5/8 giây.
Mỗi lần bạn gửi tin nhắn: Đầu tiên, hãy kiểm tra xem last_send
8 giây trước. Nếu vậy, hãy đổ đầy xô (đặt thành 5 mã thông báo). Thứ hai, nếu có mã thông báo trong nhóm, hãy gửi tin nhắn (nếu không, thả / chờ / v.v.). Thứ ba, thiết lập last_send
đến bây giờ.
Điều đó sẽ làm việc cho kịch bản đó.
Tôi thực sự đã viết một bot IRC bằng cách sử dụng một chiến lược như thế này (cách tiếp cận đầu tiên). Nó ở Perl, không phải Python, nhưng đây là một số mã để minh họa:
Phần đầu tiên ở đây xử lý việc thêm mã thông báo vào nhóm. Bạn có thể thấy tối ưu hóa việc thêm mã thông báo dựa trên thời gian (dòng thứ 2 đến dòng cuối cùng) và sau đó dòng cuối cùng kẹp nội dung xô tối đa (MESSAGE_BURST)
my $start_time = time;
...
# Bucket handling
my $bucket = $conn->{fujiko_limit_bucket};
my $lasttx = $conn->{fujiko_limit_lasttx};
$bucket += ($start_time-$lasttx)/MESSAGE_INTERVAL;
($bucket <= MESSAGE_BURST) or $bucket = MESSAGE_BURST;
$ Conn là một cấu trúc dữ liệu được truyền qua. Đây là một phương thức chạy thường xuyên (nó sẽ tính toán khi lần sau nó sẽ có việc gì đó và ngủ lâu hoặc cho đến khi nó có lưu lượng truy cập mạng). Phần tiếp theo của phương thức xử lý việc gửi. Nó khá phức tạp, bởi vì các tin nhắn có các ưu tiên liên quan đến chúng.
# Queue handling. Start with the ultimate queue.
my $queues = $conn->{fujiko_queues};
foreach my $entry (@{$queues->[PRIORITY_ULTIMATE]}) {
# Ultimate is special. We run ultimate no matter what. Even if
# it sends the bucket negative.
--$bucket;
$entry->{code}(@{$entry->{args}});
}
$queues->[PRIORITY_ULTIMATE] = [];
Đó là hàng đầu tiên, được chạy không có vấn đề gì. Ngay cả khi nó được kết nối của chúng tôi bị giết vì lũ lụt. Được sử dụng cho những việc cực kỳ quan trọng, như phản hồi PING của máy chủ. Tiếp theo, phần còn lại của hàng đợi:
# Continue to the other queues, in order of priority.
QRUN: for (my $pri = PRIORITY_HIGH; $pri >= PRIORITY_JUNK; --$pri) {
my $queue = $queues->[$pri];
while (scalar(@$queue)) {
if ($bucket < 1) {
# continue later.
$need_more_time = 1;
last QRUN;
} else {
--$bucket;
my $entry = shift @$queue;
$entry->{code}(@{$entry->{args}});
}
}
}
Cuối cùng, trạng thái nhóm được lưu trở lại cấu trúc dữ liệu $ Conn (thực tế là một lát sau trong phương thức; trước tiên, nó sẽ tính toán thời gian nó sẽ có nhiều công việc hơn)
# Save status.
$conn->{fujiko_limit_bucket} = $bucket;
$conn->{fujiko_limit_lasttx} = $start_time;
Như bạn có thể thấy, mã xử lý xô thực tế rất nhỏ - khoảng bốn dòng. Phần còn lại của mã là xử lý hàng đợi ưu tiên. Bot có các hàng đợi ưu tiên để ví dụ, ai đó trò chuyện với nó không thể ngăn nó thực hiện các nhiệm vụ cấm / cấm quan trọng.