Di sini algoritma paling sederhana , jika Anda ingin hanya mengirim pesan ketika mereka datang terlalu cepat (daripada mengantri, itu masuk akal karena antriannya bisa menjadi besar secara sewenang-wenang):
rate = 5.0; // unit: messages
per = 8.0; // unit: seconds
allowance = rate; // unit: messages
last_check = now(); // floating-point, e.g. usec accuracy. Unit: seconds
when (message_received):
current = now();
time_passed = current - last_check;
last_check = current;
allowance += time_passed * (rate / per);
if (allowance > rate):
allowance = rate; // throttle
if (allowance < 1.0):
discard_message();
else:
forward_message();
allowance -= 1.0;
Tidak ada struktur data, pengatur waktu, dll. Dalam solusi ini dan ini berfungsi dengan baik :) Untuk melihat ini, 'kelonggaran' tumbuh pada kecepatan 5/8 unit per detik paling banyak, yaitu paling banyak lima unit per delapan detik. Setiap pesan yang diteruskan mengurangi satu unit, sehingga Anda tidak dapat mengirim lebih dari lima pesan per setiap delapan detik.
Perhatikan bahwa rate
harus berupa bilangan bulat, yaitu tanpa bagian desimal non-nol, atau algoritme tidak akan berfungsi dengan benar (laju aktual tidak akan rate/per
). Misalnya rate=0.5; per=1.0;
tidak berfungsi karena allowance
tidak akan pernah tumbuh menjadi 1,0. Tapi rate=1.0; per=2.0;
berfungsi dengan baik.
allowance
. Ukuran ember adalahrate
. Theallowance += …
garis adalah optimalisasi menambahkan token setiap tingkat ÷ per detik.Gunakan dekorator ini @RateLimited (ratepersec) sebelum fungsi Anda yang enqueues.
Pada dasarnya, ini memeriksa apakah 1 / tingkat detik telah berlalu sejak terakhir kali dan jika tidak, menunggu sisa waktu, jika tidak maka tidak akan menunggu. Ini secara efektif membatasi Anda untuk menilai / detik. Dekorator dapat diterapkan ke fungsi apa pun yang Anda inginkan dibatasi tarif.
Dalam kasus Anda, jika Anda ingin maksimum 5 pesan per 8 detik, gunakan @RateLimited (0,625) sebelum fungsi sendToQueue Anda.
sumber
time.clock()
tidak memiliki resolusi yang cukup pada sistem saya, jadi saya mengadaptasi kode dan mengubahnya untuk digunakantime.time()
time.clock()
, yang mengukur waktu CPU yang telah berlalu. Waktu CPU dapat berjalan lebih cepat atau lebih lambat daripada waktu "sebenarnya". Anda ingin menggunakantime.time()
sebagai gantinya, yang mengukur waktu dinding (waktu "aktual").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, aturlast_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)
$ 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.
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:
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)
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.
sumber
untuk memblokir pemrosesan hingga pesan dapat dikirim, sehingga mengantri pesan lebih lanjut, solusi indah antti juga dapat dimodifikasi seperti ini:
hanya menunggu sampai ada cukup uang untuk mengirim pesan. untuk tidak memulai dengan dua kali kurs, tunjangan juga dapat diinisialisasi dengan 0.
sumber
(1-allowance) * (per/rate)
, Anda perlu menambahkan jumlah yang samalast_check
.Pertahankan waktu pengiriman lima baris terakhir. Tahan pesan yang antri sampai waktu pesan kelima terbaru (jika ada) setidaknya 8 detik di masa lalu (dengan last_five sebagai larik waktu):
sumber
Salah satu solusinya adalah melampirkan stempel waktu untuk setiap item antrian dan membuang item setelah 8 detik berlalu. Anda dapat melakukan pemeriksaan ini setiap kali antrian ditambahkan.
Ini hanya berfungsi jika Anda membatasi ukuran antrian hingga 5 dan membuang penambahan apa pun saat antrian penuh.
sumber
Jika seseorang masih tertarik, saya menggunakan kelas callable sederhana ini dalam hubungannya dengan penyimpanan nilai kunci LRU waktunya untuk membatasi tingkat permintaan per IP. Menggunakan deque, tetapi dapat ditulis ulang untuk digunakan dengan daftar.
sumber
Hanya implementasi kode python dari jawaban yang diterima.
sumber
Bagaimana dengan ini:
sumber
Saya membutuhkan variasi dalam Scala. Ini dia:
Inilah cara penggunaannya:
sumber