Introduction
HTB (Hierarchical Token Bucket) is een classful queuing discipline die nuttig is voor snelheidsbeperking en het verwerken van bursts. Dit artikel richt zich uitsluitend op die HTB-aspecten binnen RouterOS, aangezien wij een aangepaste versie gebruiken om functies als Simple Queue en Queue Tree te leveren.
Token Bucket-algoritme (rode deel van het diagram)
Het Token Bucket algoritme is gebaseerd op de analogie van een emmer waarin tokens, uitgedrukt in bytes, met een bepaalde snelheid worden toegevoegd. De emmer zelf heeft een vastgestelde capaciteit.
Als de bucket tot de maximale capaciteit gevuld raakt, worden nieuw binnenkomende tokens verworpen.
Bucket capacity = bucket-size * max-limit
- bucket size (0..10, Standaard:0.1)
Voordat een pakket door de wachtrij mag passeren, wordt de queue bucket gecontroleerd om te zien of deze op dat moment al voldoende tokens bevat.
Zo ja, dan wordt het juiste aantal tokens verwijderd ("ingewisseld") en mag het pakket door de wachtrij passeren.
Zo niet, dan blijven de pakketten vooraan in de wachtrij staan totdat de juiste hoeveelheid tokens beschikbaar is.
In het geval van een queue-structuur met meerdere niveaus worden tokens die in een onderliggende queue worden gebruikt ook in rekening gebracht bij de bovenliggende queues. Met andere woorden: onderliggende queues 'lenen' tokens van hun bovenliggende queues.
Pakketwachtrij (blauwe deel van het diagram)
De grootte van deze pakketwachtrij, de volgorde, hoe pakketten aan deze wachtrij worden toegevoegd en wanneer pakketten worden verworpen, wordt bepaald door:
- queue-type - Wachtrij
- queue-size - Wachtrijgrootte
Selectie van de token rate (zwarte deel van het diagram)
De maximale token-rate op enig moment is gelijk aan de hoogste activiteit van deze waarden:
- limit-at (NUMBER/NUMBER): gegarandeerde upload-/downloadsnelheid naar een target.
- max-limit (NUMBER/NUMBER): maximale upload-/downloadsnelheid die is toegestaan voor een target.
- burst-limit (NUMBER/NUMBER): maximale upload-/downloadsnelheid die voor een doel is toegestaan zolang de 'burst' actief is.
burst-limit is alleen actief wanneer 'burst' in de toegestane toestand is - meer informatie hier: Queue Burst
In een geval waarin limit-at de hoogste waarde is, moeten er extra tokens worden uitgegeven om alle ontbrekende tokens te compenseren die niet van de bovenliggende wachtrij zijn geleend.
Het diagram

Bucket Size in de praktijk
Laten we een eenvoudige opstelling nemen waarbij al het verkeer van en naar één IP-adres wordt gemarkeerd met een packet-mark:
/ip/firewall/mangle
add chain=forward action=mark-connection connection-mark=no-mark src-address=192.168.88.101 new-connection-mark=pc1_conn
add chain=forward action=mark-connection connection-mark=no-mark dst-address=192.168.88.101 new-connection-mark=pc1_conn
add chain=forward action=mark-packet connection-mark=pc1_conn new-packet-mark=pc1_traffic
Standaard queue bucket
/queue/tree
add name=download parent=Local packet-mark=pc1_traffic max-limit=10M
add name=upload parent=Public packet-mark=pc1_traffic max-limit=10M
In dit geval is bucket-size=0.1, dus bucket-capacity= 0.1 x 10M = 1M.
Als de bucket vol is (dat wil zeggen dat de client de volledige capaciteit van de wachtrij enige tijd niet heeft gebruikt), kan de volgende 1Mb aan verkeer met onbeperkte snelheid door de wachtrij.
Grote queue bucket
/queue/tree
add name=download parent=Local packet-mark=pc1_traffic max-limit=10M bucket-size=10
add name=upload parent=Public packet-mark=pc1_traffic max-limit=10M bucket-size=10
Laten we dezelfde logica proberen toe te passen op een situatie waarin de bucket size op zijn maximale waarde staat:
In dit geval is bucket-size=10, dus bucket-capacity= 10 x 10M = 100M.
Als de bucket vol is (dat wil zeggen dat de client de volledige capaciteit van de wachtrij enige tijd niet heeft gebruikt), kan de volgende 100Mb aan verkeer met onbeperkte snelheid door de wachtrij.
U kunt dus het volgende hebben:
- 20Mbps overdrachtssnelheid gedurende 10s.
- 60Mbps transfer burst gedurende 2s.
- 1Gbps transfer burst gedurende ongeveer 100ms.
Je kunt dus zien dat de bucket een soort 'burstiness' toestaat van het verkeer dat door de queue gaat. Het gedrag lijkt op de normale burst-functie, maar mist de bovengrens van de burst. Dit nadeel kan worden vermeden als we bucket size in de queue-structuur benutten:
Grote child queue bucket, kleine parent queue bucket
/queue/tree
add name=download_parent parent=Local max-limit=20M
add name=download parent=download_parent packet-mark=pc1_traffic max-limit=10M bucket-size=10
add name=upload_parent parent=Public max-limit=20M
add name=upload parent=upload_parent packet-mark=pc1_traffic max-limit=10M bucket-size=10
In dit geval:
- parent queue bucket-size=0.1, bucket-capacity= 0.1 x 20M = 2M
- onderliggende queue bucket-size=10, bucket-capacity= 10 x 10M = 100M
De parent raakt veel sneller door zijn tokens heen dan de child-wachtrij, en omdat de child-wachtrij altijd tokens leent van de parent-wachtrij, is het hele systeem beperkt tot de token-rate van de parent-wachtrij - in dit geval tot max-limit=20M. Deze snelheid wordt volgehouden totdat de child-wachtrij door zijn tokens heen raakt en beperkt wordt tot zijn eigen token-rate van 10Mbps.
Op deze manier kunnen we tot 10 seconden lang een burst van 20 Mbps hebben.
Configuration
We moeten drie basisstappen volgen om HTB te maken:
- Match and mark traffic - classificeer verkeer voor verder gebruik. Het bestaat uit een of meer matchparameters om pakketten voor de specifieke klasse te selecteren.
- Maak regels (policy) om verkeer te markeren - plaats specifieke verkeersklassen in specifieke wachtrijen en definieer de acties die voor elke klasse worden uitgevoerd.
- Koppel een policy aan een specifieke interface(-s) - voeg een policy toe voor alle interfaces (global-in, global-out of global-total), voor een specifieke interface, of voor een specifieke bovenliggende wachtrij.
Met HTB kan een hiërarchische wachtrijstructuur worden opgezet en worden de relaties tussen wachtrijen bepaald, zoals "parent-child" of "child-child".
Zodra de wachtrij ten minste één kind heeft, wordt hij een inner wachtrij. Alle wachtrijen zonder kinderen zijn leaf-wachtrijen. Leaf-wachtrijen verbruiken het daadwerkelijke verkeer, inner-wachtrijen zijn alleen verantwoordelijk voor de verkeersverdeling. Alle leaf-wachtrijen worden op gelijke basis behandeld.
In RouterOS is het noodzakelijk om de optie parent op te geven om een wachtrij als child aan een andere wachtrij toe te wijzen.
Dubbele limitering
Elke wachtrij in HTB heeft twee snelheidslimieten:
- CIR (Committed Information Rate) - (limit-at in RouterOS) in het slechtste geval krijgt de stroom hoe dan ook deze hoeveelheid verkeer (ervan uitgaande dat we daadwerkelijk zoveel data kunnen versturen).
- MIR (Maximal Information Rate) - (max-limit in RouterOS) het beste scenario, een snelheid die de flow kan bereiken als de parent van de wachtrij vrije bandbreedte heeft.
Met andere woorden, eerst wordt aan de limit-at (CIR) van alle queues voldaan, en pas daarna zullen onderliggende queues proberen de benodigde datasnelheid van hun bovenliggende queues te lenen om hun max-limit (MIR) te bereiken.
Waarschuwing CIR wordt hoe dan ook aan de bijbehorende wachtrij toegewezen (zelfs als de max-limit van de bovenliggende wachtrij wordt overschreden).
Om die reden raden wij aan om u aan deze regels te houden, zodat de dual limitation-functie optimaal (zoals bedoeld) wordt gebruikt:
- De som van de committed rates van alle children moet kleiner dan of gelijk zijn aan de hoeveelheid verkeer die voor de parents beschikbaar is;
CIR(parent)* ≥ CIR(child1) +...+ CIR(childN)* als de parent de hoofdparent is, geldt CIR(parent)=MIR(parent)
- De maximale snelheid van elke child moet kleiner dan of gelijk aan de maximale snelheid van de parent zijn.
MIR (parent) ≥ MIR(child1) & MIR (parent) ≥ MIR(child2) & ... & MIR (parent) ≥ MIR(childN)
Queue-kleuren in Winbox
- 0% - 50% van het beschikbare verkeer gebruikt - groen
- 50% - 75% van het beschikbare verkeer gebruikt - geel
- 75% - 100% van het beschikbare verkeer gebruikt - rood
Priority
We weten al dat limit-at (CIR) hoe dan ook aan alle wachtrijen wordt toegekend.
Priority is verantwoordelijk voor de verdeling van het resterende verkeer van de bovenliggende queues over de onderliggende queues, zodat deze max-limit kunnen bereiken
De wachtrij met de hogere prioriteit bereikt zijn max-limit eerder dan de wachtrij met lagere prioriteit. 8 is de laagste prioriteit en 1 is de hoogste.
Let op dat prioriteit alleen werkt:
- Voor leaf-wachtrijen heeft prioriteit in de inner-wachtrij geen betekenis.
- Als max-limit is opgegeven (niet 0).
Examples
In dit gedeelte analyseren we HTB in actie. Daarvoor nemen we één HTB-structuur en proberen we alle mogelijke situaties en functies te behandelen door de hoeveelheid inkomend verkeer die HTB moet verwerken te veranderen en enkele opties aan te passen.
Structure
Onze HTB-structuur bestaat uit 5 queues:
- Queue01 inner queue met twee children - Queue02 en Queue03
- Queue02 inner queue met twee children - Queue04 en Queue05
- Queue03 leaf queue
- Queue04 leaf queue
- Queue05 leaf queue
Queue03, Queue04, en Queue05 zijn clients die voortdurend 10Mbps nodig hebben. De uitgaande interface kan 10Mbps aan verkeer verwerken.
Voorbeeld 1: gebruikelijke situatie

- Queue01 limit-at=0Mbps max-limit=10Mbps
- Queue02 limit-at=4Mbps max-limit=10Mbps
- Queue03 limit-at=6Mbps max-limit=10Mbps priority=1
- Queue04 limit-at=2Mbps max-limit=10Mbps priority=3
- Queue05 limit-at=2Mbps max-limit=10Mbps priority=5
Resultaat van voorbeeld 1
- Queue03 krijgt 6Mbps.
- Queue04 krijgt 2Mbps.
- Queue05 ontvangt 2Mbps.
- Verduidelijking: HTB is zo opgebouwd dat de hoofdwachtrij, nadat aan alle limit-at-waarden is voldaan, geen doorvoer meer heeft om te verdelen.
Voorbeeld 2: gebruikelijke situatie met max-limit

- Queue01 limit-at=0Mbps max-limit=10Mbps
- Queue02 limit-at=4Mbps max-limit=10Mbps
- Queue03 limit-at=2Mbps max-limit=10Mbps priority=3
- Queue04 limit-at=2Mbps max-limit=10Mbps priority=1
- Queue05 limit-at=2Mbps max-limit=10Mbps priority=5
Resultaat van voorbeeld 2
- Queue03 ontvangt 2Mbps.
- Queue04 ontvangt 6Mbps.
- Queue05 ontvangt 2Mbps.
- Verduidelijking: Nadat aan alle limit-at-waarden is voldaan, geeft HTB doorvoer aan de wachtrij met de hoogste prioriteit.
Voorbeeld 3: limit-at van een inner queue

- Queue01 limit-at=0Mbps max-limit=10Mbps.
- Queue02 limit-at=8Mbps max-limit=10Mbps.
- Queue03 limit-at=2Mbps max-limit=10Mbps priority=1.
- Queue04 limit-at=2Mbps max-limit=10Mbps priority=3.
- Queue05 limit-at=2Mbps max-limit=10Mbps priority=5.
Resultaat van voorbeeld 3
- Queue03 ontvangt 2Mbps.
- Queue04 ontvangt 6Mbps.
- Queue05 ontvangt 2Mbps.
- Verduidelijking: Nadat aan alle limit-at-waarden is voldaan, geeft HTB doorvoer aan de wachtrij met de hoogste prioriteit. Maar in dit geval had de inner-wachtrij Queue02 een limit-at opgegeven, waarmee 8Mbps aan doorvoer werd gereserveerd voor de wachtrijen Queue04 en Queue05. Van deze twee heeft Queue04 de hoogste prioriteit, en daarom krijgt die de extra doorvoer.
Voorbeeld 4: limit-at van een leaf queue

- Queue01 limit-at=0Mbps max-limit=10Mbps.
- Queue02 limit-at=4Mbps max-limit=10Mbps.
- Queue03 limit-at=6Mbps max-limit=10Mbps priority=1.
- Queue04 limit-at=2Mbps max-limit=10Mbps priority=3.
- Queue05 limit-at=12Mbps max-limit=15Mbps priority=5.
Resultaat van voorbeeld 4
- Queue03 krijgt ongeveer 3Mbps.
- Queue04 krijgt ongeveer 1Mbps.
- Queue05 krijgt ongeveer 6Mbps.
- Verduidelijking: Alleen door aan alle limit-at-waarden te voldoen werd HTB gedwongen om 20Mbps toe te wijzen - 6Mbps aan Queue03, 2Mbps aan Queue04 en 12Mbps aan Queue05, maar onze uitgaande interface kan 10Mbps verwerken. Omdat de wachtrij van de uitgaande interface meestal FIFO is, blijft de doorvoertoewijzing de verhouding 6:2:12 of 3:1:6 aanhouden.