Token Bucket นั้นใช้งานง่าย
เริ่มต้นด้วย bucket กับโทเค็น 5 อัน
ทุก 5/8 วินาที: หากที่เก็บข้อมูลมีน้อยกว่า 5 โทเค็นให้เพิ่มอีกหนึ่งอัน
ทุกครั้งที่คุณต้องการส่งข้อความ: หากที่เก็บข้อมูลมีโทเค็น≥1ให้นำโทเค็นหนึ่งอันออกแล้วส่งข้อความ มิฉะนั้นรอ / วางข้อความ / อะไรก็ตาม
(เห็นได้ชัดว่าในรหัสจริงคุณจะต้องใช้ตัวนับจำนวนเต็มแทนโทเค็นจริงและคุณสามารถปรับขั้นตอนทุก ๆ 5/8 โดยการจัดเก็บเวลาประทับ)
อ่านคำถามอีกครั้งหากขีด จำกัด อัตราถูกรีเซ็ตอย่างสมบูรณ์ในแต่ละ 8 วินาทีจากนั้นนี่คือการปรับเปลี่ยน:
เริ่มต้นด้วยการประทับเวลาlast_send
ในเวลานานมาแล้ว (เช่นที่ยุค) เริ่มต้นด้วยที่ฝากข้อมูล 5 โทเค็นเดียวกัน
โจมตีกฎทุก ๆ 5/8 วินาที
ทุกครั้งที่คุณส่งข้อความ: อันดับแรกให้ตรวจสอบว่าlast_send
≥ 8 วินาทีก่อน ถ้าเป็นเช่นนั้นเติมที่ฝากข้อมูล (ตั้งค่าเป็น 5 โทเค็น) ประการที่สองหากมีโทเค็นอยู่ในที่ฝากข้อมูลให้ส่งข้อความ (มิฉะนั้นปล่อย / รอ / ฯลฯ ) ประการที่สามตั้งค่าlast_send
เป็นตอนนี้
ที่ควรใช้กับสถานการณ์นั้น
ฉันเคยเขียนบอท IRC โดยใช้กลยุทธ์แบบนี้ (แนวทางแรก) มันอยู่ใน Perl ไม่ใช่ Python แต่นี่คือโค้ดที่จะอธิบาย:
ส่วนแรกที่นี่จัดการการเพิ่มโทเค็นลงในที่ฝากข้อมูล คุณสามารถดูการเพิ่มประสิทธิภาพของการเพิ่มโทเค็นตามเวลา (บรรทัดที่ 2 ถึงบรรทัดสุดท้าย) และจากนั้นบรรทัดสุดท้ายจะยึดเนื้อหาที่ฝากข้อมูลไว้สูงสุด (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 เป็นโครงสร้างข้อมูลที่ส่งผ่านไปมา นี่เป็นวิธีที่ทำงานอยู่เป็นประจำ (จะคำนวณเมื่อในครั้งถัดไปที่จะมีบางอย่างที่ต้องทำและจะพักนานหรือจนกว่าจะได้รับปริมาณการใช้เครือข่าย) ส่วนถัดไปของวิธีการจัดการการส่ง มันค่อนข้างซับซ้อนเนื่องจากข้อความมีลำดับความสำคัญเกี่ยวข้องกับพวกเขา
# 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] = [];
นั่นคือคิวแรกซึ่งทำงานไม่ว่าอะไรจะเกิดขึ้น แม้ว่ามันจะทำให้การเชื่อมต่อของเราถูกฆ่าเพราะน้ำท่วม ใช้สำหรับสิ่งที่สำคัญมากเช่นการตอบสนองต่อ PING ของเซิร์ฟเวอร์ ถัดไปส่วนที่เหลือของคิว:
# 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}});
}
}
}
ในที่สุดสถานะฝากข้อมูลจะถูกบันทึกกลับไปที่โครงสร้างข้อมูล $ conn (จริง ๆ แล้วจะเป็นบิตต่อมาในวิธีการนั้นก่อนอื่นจะคำนวณว่าอีกไม่นานมันจะมีงานมากขึ้น)
# Save status.
$conn->{fujiko_limit_bucket} = $bucket;
$conn->{fujiko_limit_lasttx} = $start_time;
อย่างที่คุณเห็นรหัสการจัดการถังจริงมีขนาดเล็กมาก - ประมาณสี่บรรทัด ส่วนที่เหลือของรหัสคือการจัดการคิวลำดับความสำคัญ บอทมีคิวที่มีลำดับความสำคัญสูงเช่นมีคนพูดคุยกับมันไม่สามารถป้องกันไม่ให้ทำหน้าที่เตะ / แบนที่สำคัญ