Token Bucket cukup mudah diterapkan.
Mulailah dengan ember dengan 5 token.
Setiap 5/8 detik: Jika ember memiliki kurang dari 5 token, tambahkan satu.
Setiap kali Anda ingin mengirim pesan: Jika ember memiliki ≥1 token, ambil satu token dan kirim pesan. Kalau tidak, tunggu / jatuhkan pesan / apa pun.
(jelas, dalam kode aktual, Anda akan menggunakan penghitung integer alih-alih token nyata dan Anda dapat mengoptimalkan setiap langkah 5/8 dengan menyimpan cap waktu)
Membaca pertanyaan lagi, jika batas nilai disetel penuh setiap 8 detik, maka berikut ini modifikasi:
Mulailah dengan stempel waktu,, last_send
pada waktu yang lama (misalnya, di zaman). Juga, mulailah dengan ember 5-token yang sama.
Pukul setiap aturan 5/8 detik.
Setiap kali Anda mengirim pesan: Pertama, periksa apakah last_send
≥ 8 detik yang lalu. Jika demikian, isi ember (atur ke 5 token). Kedua, jika ada token dalam ember, kirim pesan (jika tidak, drop / tunggu / dll.). Ketiga, atur last_send
sekarang.
Itu harus bekerja untuk skenario itu.
Saya sebenarnya telah menulis bot IRC menggunakan strategi seperti ini (pendekatan pertama). Ini dalam Perl, bukan Python, tapi di sini ada beberapa kode untuk menggambarkan:
Bagian pertama di sini menangani penambahan token ke ember. Anda dapat melihat pengoptimalan penambahan token berdasarkan waktu (baris 2 hingga baris terakhir) dan kemudian baris terakhir menjepit konten bucket ke maksimum (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 adalah struktur data yang diedarkan. Ini ada di dalam metode yang berjalan secara rutin (menghitung kapan waktu berikutnya ada sesuatu yang harus dilakukan, dan tidur selama itu atau sampai mendapat lalu lintas jaringan). Bagian selanjutnya dari metode ini menangani pengiriman. Agak rumit, karena pesan memiliki prioritas yang terkait dengannya.
# 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] = [];
Itu antrian pertama, yang dijalankan tidak peduli apa. Bahkan jika itu membuat koneksi kita terbunuh karena banjir. Digunakan untuk hal-hal yang sangat penting, seperti menanggapi PING server. Selanjutnya, sisa antrian:
# 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}});
}
}
}
Akhirnya, status bucket disimpan kembali ke struktur data $ conn (sebenarnya sedikit kemudian dalam metode; pertama-tama menghitung seberapa cepat itu akan memiliki lebih banyak pekerjaan)
# Save status.
$conn->{fujiko_limit_bucket} = $bucket;
$conn->{fujiko_limit_lasttx} = $start_time;
Seperti yang Anda lihat, kode penanganan bucket sebenarnya sangat kecil - sekitar empat baris. Sisa kode adalah penanganan antrian prioritas. Bot memiliki antrian prioritas sehingga misalnya, seseorang yang mengobrol dengannya tidak dapat mencegahnya melakukan tugas tendangan / larangan penting.