Throttling in Java

vorhergehende Artikel in: Java Komponenten
25.09.2023

Während meiner Experimente mit der SpaceTraders API stieß ich auf den Auslöser einer weiteren kleinen Fingerübung in Java:

Dabei handelte es sich um den Fakt, dass die SpaceTraders API es nicht erlaubt, beliebig viele Anfragen pro Zeitenheit zu stellen. Zwischen zwei Anfragen muss eine gewisse Mindestzeitspanne liegen. Meine Idee, dies umzusetzen bestand darin, in meinem Code die API nicht direkt aufzurufen, sondern Datenobjekte, die API-Calls darstellen zu befüllen und diese in eine Warteschlange einzustellen.

Am anderen Ende der Warteschlange sollte die Komponente, die mit der eigentlichen API kommuniziert diese Objekte entnehmen und daraus die korrekten API-Calls erzeugen und ausführen. Die gesamte Interaktion mit der API würde dadurch asynchron verlaufen - damit würde die GUI auch sofort responsiver werden, da sie nicht mehr auf die Ergebnisse des API-Calls warten müsste, sondern sofort nach Einstellen des Objekts in die Warteschlange weiterarbeiten könnte. Das Ergebnis der API-Calls müsste dann in Callbacks verarbeitet werden.

Und das eigentliche Problem: Sicherzustellen - dass die API nicht mit Requests überrannt wird - könnte dan an zentraler Stelle erledigt werden: Die Warteschlange bräuchte ein Ventil, aus dem die Objekte nur mit einer einstellbaren maximalen Frequenz "herauströpfeln".

Eine Implementierung eines solchen Ventils ist hier angegeben: Es vermittelt zwischen zwei Warteschlangen: Es kopiert Elemente aus der Eingangswarteschlange in die Ausgangswarteschlange wobei die Frequenz konfigurierbar ist. es ist allerdings möglich, Bursts zu definieren: Damit ist es möglich, zu spezifizieren, dass die Frequenz zwar maximal drei Ereignisse pro Sekunde beträgt, allerdings in Ausnahmen bis zu 5 Ereignisse pro Sekunde möglich sind (CubbyHoles sind hier Warteschlangenimplementierungen):

import de.netsysit.util.threads.CubbyHole;
import java.time.Clock;

public class TimedThrottleValve<T> extends java.lang.Object implements java.lang.Runnable { private final static org.slf4j.Logger CLASS_LOGGER =org.slf4j.LoggerFactory.getLogger(TimedThrottleValve.class); private final static org.slf4j.Logger EXCEPTION_LOGGER =org.slf4j.LoggerFactory.getLogger("ExceptionCatcher"); private final long leastTimeBetweenTasks; private long lastGetTimestamp; private long burst; private final de.netsysit.util.threads.CubbyHole<T> entry; private final de.netsysit.util.threads.CubbyHole<T> exit; private long burstcounter;

public TimedThrottleValve(CubbyHole<T> entry, CubbyHole<T> exit, long leastTimeBetweenTasks) { this(entry,exit,leastTimeBetweenTasks,1); } public TimedThrottleValve(CubbyHole<T> entry, CubbyHole<T> exit, long leastTimeBetweenTasks, long burst) { super(); if(leastTimeBetweenTasks<0) throw new java.lang.IllegalArgumentException("leastTimeBetweenTasks must not be less than 0!"); if(burst<1) throw new java.lang.IllegalArgumentException("burst must not be less than 1!"); this.entry = entry; this.exit = exit; this.leastTimeBetweenTasks=leastTimeBetweenTasks; this.burst=burst; }

@Override public void run() { while(true) { try { T item=entry.get(); if(burstcounter<burst) ++burstcounter; long now = Clock.systemDefaultZone().millis(); long span = now - lastGetTimestamp; CLASS_LOGGER.debug("span {} burst{} burstcounter {}", span,burst,burstcounter); if (span >= leastTimeBetweenTasks) burstcounter=0; if(burstcounter==burst) { if (span < leastTimeBetweenTasks) { long l = leastTimeBetweenTasks - (now - lastGetTimestamp); CLASS_LOGGER.debug("Sleeping for {} millis...", l); java.lang.Thread.currentThread().sleep(leastTimeBetweenTasks); } } exit.put(item); lastGetTimestamp=Clock.systemDefaultZone().millis(); } catch(java.lang.InterruptedException exp) {

} } } }

Alle Artikel rss Wochenübersicht Monatsübersicht Codeberg Repositories Mastodon Über mich home xmpp


Vor 5 Jahren hier im Blog

  • Synchronisierung von Lorenz-Systemen III

    23.10.2020

    Nachdem ich in einem vorhergehenden Artikel auf das Problem des kleinen Parameterraums im Zusammenhang mit der Nutzung synchronisierter chaotischer Systeme hingewiesen hatte will ixch hier untersuchen, wie sensibel solche Systeme auf Abweichungen der Parameterwerte zwischen treibendem und getriebenen System reagieren

    Weiterlesen

Neueste Artikel

  • Plugin zur Arbeit mit Markdown für NeoVim

    Ich habe neulich beschrieben, dass ich aktuell mehr und mehr bemerke, dass Dinge, für die ich in meinem NeoVim-Setup Plugins benutzt habe sehr gut auch mit Bordmitteln funktionieren.

    Weiterlesen
  • Raspbian Upgrade von 11 (Bullseye) nach 12 (Bookworm)

    Ich habe neulich wieder einmal eine Upgrade- und Backup-Sitzung mit meinen diversen Linuxinstallationen veranstaltet. Der Zeitpunkt schien mir gekommen, da es eine neue stable Variante von Debian (Trixie) gibt.

    Weiterlesen
  • Meine praktischen Erfahrungen mit ollama (llava)

    Ich diskutiere immer wieder gern über das was heute Machine Intelligence oder Artificial Intelligence ( oder wie die ganzen anderen hohlen Phrasen heißen, die dafür heutzutage als Buzzwords missbraucht werden). Das geschieht online, in meinem $dayjob oder auch privat. Meine Meinung steht fest: das ist alles Quatsch und steht in keiner Relation zum Nutzen

    Weiterlesen

Manche nennen es Blog, manche Web-Seite - ich schreibe hier hin und wieder über meine Erlebnisse, Rückschläge und Erleuchtungen bei meinen Hobbies.

Wer daran teilhaben und eventuell sogar davon profitieren möchte, muss damit leben, daß ich hin und wieder kleine Ausflüge in Bereiche mache, die nichts mit IT, Administration oder Softwareentwicklung zu tun haben.

Ich wünsche allen Lesern viel Spaß und hin und wieder einen kleinen AHA!-Effekt...

PS: Meine öffentlichen Codeberg-Repositories findet man hier.