Best Practices für RTB-Anwendungen

In diesem Leitfaden werden Best Practices erläutert, die Sie bei der Entwicklung von Anwendungen gemäß dem EZG-Protokoll berücksichtigen sollten.

Verbindungen verwalten

Verbindungen aufrechterhalten

Das Herstellen einer neuen Verbindung erhöht die Latenzen und beansprucht weit mehr Ressourcen an beiden Enden als die Wiederverwendung einer vorhandenen Verbindung. Wenn Sie weniger Verbindungen beenden, reduzieren Sie die Anzahl der Verbindungen, die noch einmal geöffnet werden müssen.

Erstens erfordert jede neue Verbindung einen zusätzlichen Netzwerk-Roundtrip. Da wir Verbindungen auf Anfrage herstellen, hat die erste Anfrage für eine Verbindung eine kürzere effektive Frist und es ist wahrscheinlicher, dass es zu einer Zeitüberschreitung kommt als bei nachfolgenden Anfragen. Zusätzliche Zeitüberschreitungen erhöhen die Fehlerrate, was dazu führen kann, dass Ihre Gebotsfunktion gedrosselt wird.

Zweitens erzeugen viele Webserver für jede hergestellte Verbindung einen dedizierten Worker-Thread. Um die Verbindung zu schließen und neu zu erstellen, muss der Server also einen Thread herunterfahren und verwerfen, einen neuen Thread zuweisen, ihn ausführbar machen und den Verbindungsstatus aufbauen, bevor er die Anfrage schließlich verarbeiten kann. Das ist viel unnötiger Aufwand.

Verbindungen vermeiden

Beginnen Sie mit der Optimierung des Verbindungsverhaltens. Die meisten Standardeinstellungen von Servern sind auf Umgebungen mit einer großen Anzahl von Clients zugeschnitten, die jeweils nur eine kleine Anzahl von Anfragen stellen. Bei RTB sendet dagegen ein kleiner Pool von Computern Anfragen im Auftrag einer großen Anzahl von Browsern. Unter diesen Bedingungen ist es sinnvoll, Verbindungen so oft wie möglich wiederzuverwenden. Wir empfehlen Folgendes:

  • Zeitlimit bei Inaktivität auf 2,5 Minuten.
  • Maximale Anzahl von Anfragen für eine Verbindung auf den höchstmöglichen Wert.
  • Maximale Anzahl von Verbindungen auf den höchsten Wert, den Ihr RAM aufnehmen kann. Achten Sie dabei darauf, dass sich die Anzahl der Verbindungen nicht zu nah an diesem Wert annähert.

In Apache bedeutet dies beispielsweise, dass KeepAliveTimeout auf 150, MaxKeepAliveRequests auf null und MaxClients auf einen Wert gesetzt werden, der vom Servertyp abhängt.

Sobald das Verbindungsverhalten optimiert ist, sollten Sie darauf achten, dass Ihr Bietercode Verbindungen nicht unnötig schließt. Wenn Sie beispielsweise Front-End-Code haben, der im Falle von Back-End-Fehlern oder Zeitüberschreitungen die Standardantwort „kein Gebot“ zurückgibt, achten Sie darauf, dass der Code eine Antwort zurückgibt, ohne die Verbindung zu beenden. So vermeiden Sie, dass bei einer Überlastung der Gebotsfunktion Verbindungen geschlossen werden und die Anzahl der Zeitüberschreitungen zunimmt, wodurch Ihre Gebotsfunktion gedrosselt wird.

Verbindungen ausgewogen halten

Wenn Authorized Buyers über einen Proxyserver eine Verbindung zu den Servern Ihrer Gebotsfunktion herstellt, kann es zu einem ungleichmäßigen Verhältnis zwischen den Verbindungen im Laufe der Zeit kommen. Denn da nur die IP-Adresse des Proxyservers bekannt ist, kann in Authorized Buyers nicht festgestellt werden, welcher Bieterserver die einzelnen Callouts erhält. Während Authorized Buyers Verbindungen herstellt und trennt und die Server des Bieters neu gestartet werden, kann die Anzahl der zugeordneten Verbindungen im Laufe der Zeit stark schwanken.

Wenn einige Verbindungen stark ausgelastet sind, bleiben andere geöffnete Verbindungen möglicherweise größtenteils inaktiv, da sie zu diesem Zeitpunkt nicht benötigt werden. Wenn sich der Traffic in Authorized Buyers ändert, können inaktive Verbindungen aktiv und aktive Verbindungen inaktiv werden. Dies kann zu einer ungleichmäßigen Last auf Ihren Gebotsservern führen, wenn die Verbindungen schlecht gruppiert sind. Google versucht, dies zu verhindern, indem alle Verbindungen nach 10.000 Anfragen beendet werden, um heiße Verbindungen im Laufe der Zeit automatisch auszugleichen. Wenn Sie immer noch feststellen, dass der Traffic in Ihrer Umgebung ungleichmäßig ist, können Sie weitere Schritte unternehmen:

  1. Wählen Sie das Back-End pro Anfrage und nicht einmal pro Verbindung aus, wenn Sie Front-End-Proxys verwenden.
  2. Geben Sie eine maximale Anzahl von Anfragen pro Verbindung an, wenn Sie Verbindungen über einen Hardware-Load-Balancer oder eine Firewall weiterleiten und die Zuordnung wieder hergestellt ist, sobald die Verbindungen hergestellt sind. Beachten Sie, dass Google bereits eine Obergrenze von 10.000 Anfragen pro Verbindung festgelegt hat. Sie müssen also nur einen strikteren Wert angeben, wenn Sie dennoch feststellen, dass Hot-Verbindungen in Ihrer Umgebung geclustert werden. Legen Sie in Apache beispielsweise MaxKeepAliveRequests auf 5.000 fest.
  3. Konfigurieren Sie die Server des Bieters so, dass sie ihre Anfrageraten überwachen und einige ihrer eigenen Verbindungen schließen, wenn sie im Vergleich zu den Peers regelmäßig zu viele Anfragen verarbeiten.

Ordnungsgemäßer Umgang mit Überlastung

Im Idealfall sind die Kontingente so hoch, dass Ihre Gebotsfunktion alle Anfragen erhalten kann, die sie verarbeiten kann, aber nicht mehr als diese Anzahl. In der Praxis ist es eine schwierige Aufgabe, Kontingente auf einem optimalen Niveau zu halten, und Überlastungen können aus verschiedenen Gründen auftreten: ein Back-End, das während der Spitzenzeiten ausfällt, eine Traffic-Mischung, die dazu führt, dass für jede Anfrage mehr Verarbeitung erforderlich ist, oder ein Kontingentwert, das einfach zu hoch angesetzt ist. Daher lohnt es sich, zu berücksichtigen, wie sich die Gebotsfunktion bei zu vielen eingehenden Zugriffen verhält.

Um vorübergehende Traffic-Veränderungen (bis zu einer Woche) zwischen Regionen zu berücksichtigen (insbesondere zwischen Asien und den USA sowie dem Osten und Westen der USA), empfehlen wir eine 15 %-Abweichung zwischen dem 7-Tage-Spitzenwert und der Anzahl der Abfragen pro Sekunde pro Media-Einkaufszentrale.

In Bezug auf das Verhalten bei hoher Last lassen sich Bieter in drei große Kategorien einteilen:

Der Bieter, der auf alles antwortet

Diese Gebotsanwendung ist zwar einfach zu implementieren, schneidet aber am schlechtesten ab, wenn sie überlastet ist. Sie versucht einfach, auf jede eingehende Gebotsanfrage zu antworten und stellt alle Anfragen, die nicht sofort verarbeitet werden können, in die Warteschlange. Das daraus resultierende Szenario sieht oft so aus:

  • Mit steigender Anfragerate nehmen auch die Anfragelatenzen zu, bis bei allen Anfragen eine Zeitüberschreitung auftritt.
  • Die Latenzen steigen drastisch an, wenn die Callout-Raten sich dem Spitzenwert nähern.
  • Es erfolgt eine Drosselung, wodurch die Anzahl der zulässigen Callouts stark abnimmt.
  • Die Latenzen erholen sich wieder, was dazu führt, dass die Drosselung reduziert wird.
  • Der Zyklus beginnt von vorn.

Der Latenzdiagramm für diese Gebotsanwendung ähnelt einem sehr steilen Sägezahnmuster. Alternativ kann der Server bei Anfragen in der Warteschlange mit dem Paging des Arbeitsspeichers beginnen oder andere Aktionen ausführen, die eine langfristige Verlangsamung verursachen. Die Latenzen werden dann erst nach Ablauf der Spitzenzeiten wiederhergestellt, was zu niedrigeren Callout-Raten während der gesamten Spitzenzeit führt. In beiden Fällen werden weniger Callouts erstellt oder darauf reagiert, als wenn das Kontingent einfach auf einen niedrigeren Wert gesetzt worden wäre.

Die Gebotsfunktion, bei der es zu Überlast kommt

Diese Gebotsfunktion akzeptiert Callouts bis zu einer bestimmten Rate und gibt dann für einige Callouts Fehler zurück. Dies kann durch interne Zeitüberschreitungen, die Deaktivierung von Verbindungswarteschlangen (gesteuert durch ListenBackLog bei Apache), die Implementierung eines probabilistischen Drop-Modus, wenn die Auslastung oder die Latenzen zu hoch werden, oder ein anderer Mechanismus erfolgen. Wenn Google eine Fehlerrate über 15 % feststellt, beginnen wir mit der Drosselung. Im Gegensatz zur Gebotsanwendung, die auf alles antwortet, „kürzt sich die Verluste“, sodass sie sich sofort erholen kann, wenn die Anfrageraten sinken.

Der Latenzdiagramm für diese Gebotsfunktion ähnelt einem flachen Sägezahnmuster bei Überlastungen, das sich in der Nähe der maximal zulässigen Rate befindet.

Die Gebotsfunktion, die bei Überlast kein Gebot abgibt

Diese Gebotsfunktion akzeptiert Callouts bis zu einer bestimmten Rate und gibt bei Überlastung die Antwort „Kein Gebot“ zurück. Ähnlich wie die Gebotsfunktion "Fehler bei Überlast" kann dies auf verschiedene Arten implementiert werden. Der Unterschied besteht darin, dass kein Signal an Google zurückgegeben wird und Callouts daher nicht gedrosselt werden. Die Überlastung wird von den Front-End-Maschinen absorbiert, die nur den Traffic zulassen, den sie verarbeiten können, zu den Back-Ends.

Das Latenzdiagramm für diese Gebotsfunktion zeigt ein Plateau, das zu Spitzenzeiten (künstlich) nicht mehr parallel zur Anfragerate aufhört. Ein entsprechender Rückgang des Anteils der Antworten, die ein Gebot enthalten, ist ebenfalls zu sehen.

Wir empfehlen, den Ansatz „Fehler bei Überlast“ mit dem Ansatz „Kein Gebot bei Überlast“ zu kombinieren:

  • Stellen Sie die Front-Ends zu viel bereit und richten Sie sie bei Überlastung auf Fehler ein, um die Anzahl der Verbindungen zu maximieren, auf die sie in irgendeiner Form antworten können.
  • Wenn bei Überlast ein Fehler auftritt, können die Front-End-Maschinen eine vorformulierte Antwort ohne Gebot verwenden und müssen die Anfrage nicht parsen.
  • Implementieren Sie die Systemdiagnose der Back-Ends. Wenn beispielsweise keine ausreichenden Kapazitäten verfügbar sind, wird die Antwort "kein Gebot" zurückgegeben.

Dadurch kann ein Teil der Überlast absorbiert werden und die Back-Ends haben die Möglichkeit, auf genau so viele Anfragen zu antworten, wie sie verarbeiten können. Sie können sich dies als „Kein Gebot bei Überlast“ vorstellen, wenn Front-End-Maschinen auf „Fehler bei Überlast“ zurückgreifen, wenn die Anzahl der Anfragen deutlich höher als erwartet ist.

Wenn Sie eine Gebotsfunktion verwenden, die auf alles antwortet, wandeln Sie sie in eine Gebotsanwendung um, die bei Überlastung fehlerhaft ist, indem Sie das Verbindungsverhalten so anpassen, dass eine Überlastung verhindert wird. Dies führt zwar dazu, dass mehr Fehler zurückgegeben werden, aber es werden Zeitüberschreitungen reduziert und verhindert, dass der Server in einen Zustand gelangt, in dem er nicht auf Anfragen antworten kann.

Auf Pings reagieren

Für die Fehlerbehebung ist es überraschend wichtig, dass Ihre Gebotsanwendung auf Ping-Anfragen reagieren kann, ohne dass die Verbindungsverwaltung an sich verwendet wird. Google verwendet Ping-Anfragen für die Plausibilitätsprüfung und Fehlerbehebung des Bieterstatus, das Verhalten bei Verbindungsabbrüchen, die Latenz und vieles mehr. Ping-Anfragen haben die folgende Form:

Google

id: "\3503cg\3023P\364\230\240\270\020\255\002\231\314\010\362\347\351\246\357("
is_test: true
is_ping: true

OpenRTB-JSON

"id": "4YB27BCXimH5g7wB39nd3t"

OpenRTB-Protokollzwischenspeicher

id: "7xd2P2M7K32d7F7Y50p631"

Anders als erwartet enthält die Ping-Anfrage keine Anzeigenflächen. Wie oben beschrieben, sollten Sie die Verbindung nach dem Antworten auf eine Ping-Anfrage nicht trennen.

Peering erwägen

Eine weitere Möglichkeit, die Netzwerklatenz oder -variabilität zu verringern, ist eine Peering-Verbindung mit Google. Mit Peering können Sie den Pfad optimieren, den Traffic zu Ihrer Gebotsfunktion führt. Die Verbindungsendpunkte bleiben gleich, die Zwischenlinks ändern sich jedoch. Weitere Informationen finden Sie im Leitfaden zu Peering. Der Grund, das Peering als Best Practice zu betrachten, lässt sich so zusammenfassen:

  • Im Internet werden Transitverbindungen in erster Linie durch „Hot-Potato-Routing“ ausgewählt. Dabei wird der nächstgelegene Link außerhalb unseres Netzwerks gefunden, der ein Paket zu seinem Ziel erhalten kann, und das Paket über diesen Link leiten. Durchläuft Traffic einen Backbone-Bereich eines Anbieters, zu dem wir viele Peering-Verbindungen haben, befindet sich die ausgewählte Verbindung wahrscheinlich in der Nähe des Startpunkts des Pakets. Danach haben wir keine Kontrolle mehr über die Route, die das Paket an die Gebotsfunktion folgt. Daher kann es auf dem Weg an andere autonome Systeme (Netzwerke) zurückgesendet werden.

  • Im Gegensatz dazu werden bei einer Direct Peering-Vereinbarung Pakete immer über einen Peering-Link gesendet. Unabhängig davon, wo das Paket den Ursprung hat, durchläuft es Links, die Google gehören oder die Google freigibt, bis es den gemeinsamen Peering-Punkt erreicht, der sich in der Nähe des Bieterstandorts befinden sollte. Die Rückfahrt beginnt mit einem kurzen Sprung zum Google-Netzwerk und bleibt für den Rest des Weges im Google-Netzwerk. Da der größte Teil der Fahrt in der von Google verwalteten Infrastruktur bleibt, wird sichergestellt, dass das Paket eine Route mit niedriger Latenz nimmt und große potenzielle Schwankungen vermieden werden.

Statisches DNS senden

Wir empfehlen Käufern, immer ein einzelnes statisches DNS-Ergebnis an Google zu senden und die Zustellung des Traffics von Google zu übernehmen.

Im Folgenden sind zwei gängige Vorgehensweisen von DNS-Servern von Bietern für das Load-Balancing oder die Verwaltung der Verfügbarkeit aufgeführt:

  1. Der DNS-Server gibt als Antwort auf eine Abfrage eine Adresse oder einen Teil der Adressen aus und durchläuft diese Antwort dann auf irgendeine Weise.
  2. Der DNS-Server antwortet immer mit demselben Satz von Adressen, wechselt in der Antwort jedoch die Reihenfolge der Adressen in Zyklen.

Die erste Methode ist schlecht beim Load-Balancing, da auf mehreren Ebenen des Stacks viel Caching vorhanden ist. Versuche, das Caching zu umgehen, führen wahrscheinlich nicht zu den bevorzugten Ergebnissen, da Google der Gebotsfunktion die DNS-Auflösungszeit in Rechnung stellt.

Bei der zweiten Technik wird kein Load-Balancing erreicht, da Google eine IP-Adresse zufällig aus der DNS-Antwortliste auswählt, sodass die Reihenfolge in der Antwort keine Rolle spielt.

Wenn ein Bieter eine DNS-Änderung vornimmt, berücksichtigt Google die in den DNS-Einträgen festgelegte TTL(Time-to-live). Das Aktualisierungsintervall bleibt jedoch ungewiss.