Bonnes pratiques pour les applications RTB

Ce guide décrit les bonnes pratiques à suivre lors du développement d'applications conformément au protocole RTB.

Gérer les connexions

Maintenir les connexions

L'établissement d'une nouvelle connexion augmente les latences et nécessite beaucoup plus de ressources à la fois à l'origine et à la destination qu'une réutilisation d'une connexion existante. En fermant moins de connexions, vous pouvez réduire le nombre de connexions qui doivent être ouvertes à nouveau.

Tout d'abord, chaque nouvelle connexion nécessite un aller-retour réseau supplémentaire pour s'établir. Étant donné que nous établissons des connexions à la demande, la première requête sur une connexion a un délai d'expiration effectif plus court et est plus susceptible de dépasser le délai d'expiration que les requêtes ultérieures. Tout délai avant expiration supplémentaire augmente le taux d'erreur, ce qui peut entraîner la limitation de votre enchérisseur.

Deuxièmement, de nombreux serveurs Web génèrent un thread de travail dédié pour chaque connexion établie. Cela signifie que pour fermer et recréer la connexion, le serveur doit arrêter et supprimer un thread, en allouer un nouveau, le rendre exécutable et créer l'état de la connexion, avant de traiter finalement la requête. Cela représente beaucoup de frais inutiles.

Éviter de fermer les connexions

Commencez par ajuster le comportement de la connexion. La plupart des valeurs par défaut du serveur sont adaptées aux environnements comportant un grand nombre de clients, chacun effectuant un petit nombre de requêtes. À l'inverse, pour le RTB, un petit pool de machines envoie des requêtes au nom d'un grand nombre de navigateurs, relativement parlant. Dans ces conditions, il est logique de réutiliser les connexions autant de fois que possible. Nous vous recommandons de définir les éléments suivants:

  • Délai d'inactivité défini sur 2,5 minutes.
  • Nombre maximal de requêtes sur une connexion à la valeur la plus élevée possible.
  • Nombre maximal de connexions à la valeur la plus élevée que votre RAM peut accueillir, en veillant à vérifier que le nombre de connexions ne s'approche pas trop de cette valeur.

Dans Apache, par exemple, cela implique de définir KeepAliveTimeout sur 150, MaxKeepAliveRequests sur zéro et MaxClients sur une valeur qui dépend du type de serveur.

Une fois le comportement de connexion affiné, vous devez également vous assurer que votre code d'enchérisseur ne ferme pas les connexions inutilement. Par exemple, si vous disposez d'un code front-end qui renvoie une réponse par défaut "pas d'enchère" en cas d'erreurs ou de délais avant expiration du backend, assurez-vous que le code renvoie sa réponse sans fermer la connexion. Vous évitez ainsi que votre enchérisseur ne soit surchargé, que les connexions ne commencent à se fermer et que le nombre de délais d'inactivité augmente, ce qui entraîne la limitation de votre enchérisseur.

Équilibrer les connexions

Si Authorized Buyers se connecte aux serveurs de votre enchérisseur via un serveur proxy, les connexions peuvent devenir déséquilibrées au fil du temps. En effet, ne connaissant que l'adresse IP du serveur proxy, Authorized Buyers ne peut pas déterminer quel serveur d'enchérisseur reçoit chaque appel. Au fil du temps, à mesure qu'Authorized Buyers établit et ferme des connexions et que les serveurs de l'enchérisseur redémarrent, le nombre de connexions mappées à chacun peut devenir très variable.

Lorsque certaines connexions sont fortement utilisées, d'autres connexions ouvertes peuvent rester principalement inactives, car elles ne sont pas nécessaires pour le moment. À mesure que le trafic des acheteurs autorisés change, les connexions inactives peuvent devenir actives et les connexions actives peuvent devenir inactives. Cela peut entraîner des charges inégales sur vos serveurs d'enchères si les connexions sont mal regroupées. Google tente d'éviter cela en fermant toutes les connexions après 10 000 requêtes, afin de rééquilibrer automatiquement les connexions actives au fil du temps. Si le trafic reste déséquilibré dans votre environnement, vous pouvez prendre d'autres mesures:

  1. Sélectionnez le backend par requête plutôt qu'une fois par connexion si vous utilisez des proxys de frontend.
  2. Spécifiez un nombre maximal de requêtes par connexion si vous proxyez des connexions via un équilibreur de charge ou un pare-feu matériel, et que le mappage est fixe une fois les connexions établies. Notez que Google spécifie déjà une limite supérieure de 10 000 requêtes par connexion. Vous ne devriez donc fournir une valeur plus stricte que si vous constatez toujours que des connexions actives se regroupent dans votre environnement. Dans Apache, par exemple, définissez MaxKeepAliveRequests sur 5 000.
  3. Configurez les serveurs de l'enchérisseur pour surveiller ses taux de requêtes et fermer certaines de ses propres connexions s'ils gèrent constamment trop de requêtes par rapport à leurs pairs.

Gérer la surcharge de manière élégante

Dans l'idéal, les quotas doivent être suffisamment élevés pour que votre enchérisseur puisse recevoir toutes les requêtes qu'il peut traiter, mais pas plus. En pratique, maintenir les quotas à des niveaux optimaux est une tâche difficile, et des surcharges se produisent pour diverses raisons: un backend en panne pendant les heures de pointe, un mix de trafic qui change de sorte que plus de traitement soit nécessaire pour chaque requête ou une valeur de quota simplement définie trop haut. Par conséquent, il est utile de réfléchir à la façon dont votre enchérisseur se comportera en cas de trop de trafic entrant.

Pour tenir compte des fluctuations temporaires du trafic (jusqu'à une semaine) entre les régions (en particulier entre l'Asie et l'Ouest des États-Unis, et l'Est des États-Unis et l'Ouest des États-Unis), nous recommandons de prévoir une marge de 15% entre le pic sur sept jours et le débit de requêtes par seconde par zone de trading.

En termes de comportement en cas de charge importante, les enchérisseurs se répartissent en trois grandes catégories:

L'enchérisseur "répond à tout"

Bien qu'il soit simple à implémenter, cet enchérisseur est le moins performant en cas de surcharge. Il tente simplement de répondre à chaque demande d'enchères qui lui parvient, quelle que soit la situation, et met en file d'attente celles qui ne peuvent pas être traitées immédiatement. Le scénario qui s'ensuit est souvent le suivant:

  • À mesure que le taux de requêtes augmente, les latences de requêtes augmentent également, jusqu'à ce que toutes les requêtes commencent à expirer.
  • Les latences augmentent de façon spectaculaire lorsque les taux d'accroches approchent de leur pic
  • Le débit est limité, ce qui réduit considérablement le nombre d'appels autorisés.
  • Les latences commencent à se rétablir, ce qui réduit le débit limité.
  • Le cycle recommence.

Le graphique de latence de cet enchérisseur ressemble à un motif en dents de scie très raide. En outre, les requêtes mises en file d'attente entraînent le démarrage de la pagination de la mémoire ou une autre action qui provoque un ralentissement à long terme. Les latences ne se rétablissent pas du tout tant que les pics d'activité ne sont pas terminés, ce qui entraîne une baisse des taux de rappel pendant toute la période de pointe. Dans les deux cas, moins d'appels sont effectués ou répondus que si le quota avait simplement été défini sur une valeur inférieure.

L'enchérisseur "error on overload"

Cet enchérisseur accepte les accroches jusqu'à un certain tarif, puis commence à renvoyer des erreurs pour certaines accroches. Cela peut être fait via des délais avant expiration internes, en désactivant la mise en file d'attente des connexions (contrôlée par ListenBackLog sur Apache), en implémentant un mode de suppression probabiliste lorsque l'utilisation ou les latences deviennent trop élevées, ou par un autre mécanisme. Si Google observe un taux d'erreur supérieur à 15%, nous commencerons à limiter la bande passante. Contrairement à l'enchérisseur "répondre à tout", celui-ci "coupe ses pertes", ce qui lui permet de se rétablir immédiatement lorsque les taux de requêtes diminuent.

Le graphique de la latence pour cet enchérisseur ressemble à un motif en dents de scie peu profond lors des surcharges, localisé autour du débit maximal acceptable.

Enchérisseur "aucune enchère en cas de surcharge"

Ce système d'enchères accepte les accroches jusqu'à un certain débit, puis renvoie des réponses "aucune enchère" en cas de surcharge. Comme pour l'enchérisseur "erreur en cas de surcharge", cette fonctionnalité peut être implémentée de plusieurs manières. La différence ici est qu'aucun signal n'est renvoyé à Google. Nous ne limitons donc jamais les accroches. La surcharge est absorbée par les machines de front-end, qui ne permettent que le trafic qu'elles peuvent gérer de continuer vers les backends.

Le graphique de latence de cet enchérisseur montre un plateau qui (artificiellement) cesse de suivre le taux de requêtes aux heures de pointe, et une baisse correspondante de la fraction des réponses contenant une enchère.

Nous vous recommandons de combiner l'approche "erreur en cas de surcharge" avec l'approche "pas d'enchère en cas de surcharge", comme suit:

  • Surprovisionnez les interfaces et définissez-les sur "erreur en cas de surcharge" pour maximiser le nombre de connexions auxquelles elles peuvent répondre d'une manière ou d'une autre.
  • En cas d'erreur de surcharge, les machines de front-end peuvent utiliser une réponse "pas d'enchère" prédéfinie et n'ont pas besoin d'analyser la requête.
  • Implémentez une vérification de l'état des backends afin que, si aucun n'a une capacité disponible suffisante, ils renvoient une réponse "pas d'enchère".

Cela permet d'absorber une partie de la surcharge et donne aux backends la possibilité de répondre exactement au nombre de requêtes qu'ils peuvent gérer. Vous pouvez considérer cela comme une "enchère sans enchère en cas de surcharge", les machines de front-end revenant à "erreur en cas de surcharge" lorsque le nombre de requêtes est nettement supérieur à celui prévu.

Si vous disposez d'un système d'enchères "répondre à tout", envisagez de le transformer en système d'enchères "erreur en cas de surcharge" en ajustant le comportement des connexions afin qu'il refuse en fait d'être surchargé. Bien que cela entraîne le retour d'un plus grand nombre d'erreurs, cela réduit les délais avant expiration et empêche le serveur d'atteindre un état où il ne peut plus répondre à aucune requête.

Envisager un appairage

Un autre moyen de réduire la latence ou la variabilité du réseau consiste à établir un peering avec Google. Le peering permet d'optimiser le chemin emprunté par le trafic pour atteindre votre enchérisseur. Les points de terminaison de la connexion restent les mêmes, mais les maillons intermédiaires changent. Pour en savoir plus, consultez le guide de peering. Voici pourquoi le peering est une bonne pratique:

  • Sur Internet, les liens de transit sont principalement choisis via le routage "hot-potato", qui trouve le lien le plus proche en dehors de notre réseau qui peut acheminer un paquet vers sa destination, puis l'achemine via ce lien. Lorsque le trafic traverse une section de dorsale appartenant à un fournisseur avec lequel nous avons de nombreuses connexions de peering, le lien choisi est susceptible d'être proche du point de départ du paquet. Au-delà de ce point, nous n'avons aucun contrôle sur le chemin que le paquet suit jusqu'à l'enchérisseur. Il peut donc être renvoyé vers d'autres systèmes autonomes (réseaux) en cours de route.

  • En revanche, lorsqu'un accord d'appairage direct est en place, les paquets sont toujours envoyés via un lien d'appairage. Quel que soit l'origine du paquet, il traverse des liens appartenant ou loués par Google jusqu'à atteindre le point de peering partagé, qui doit se trouver à proximité de l'emplacement de l'enchérisseur. Le trajet inverse commence par un court saut vers le réseau Google et reste sur le réseau Google tout le reste du trajet. En laissant la majeure partie du trajet sur l'infrastructure gérée par Google, vous vous assurez que le paquet emprunte un itinéraire à faible latence et évitez de nombreuses variations potentielles.

Envoyer un DNS statique

Nous recommandons aux acheteurs de toujours envoyer un seul résultat DNS statique à Google et de s'appuyer sur Google pour gérer la diffusion du trafic.

Voici deux pratiques courantes des serveurs DNS des enchérisseurs lorsqu'ils tentent de charger un solde ou de gérer la disponibilité:

  1. Le serveur DNS distribue une adresse ou un sous-ensemble d'adresses en réponse à une requête, puis fait le tour de cette réponse d'une manière ou d'une autre.
  2. Le serveur DNS répond toujours avec le même ensemble d'adresses, mais fait tourner l'ordre des adresses dans la réponse.

La première technique est peu efficace pour l'équilibrage de charge, car il existe de nombreux caches à plusieurs niveaux de la pile. Les tentatives de contournement du cache ne permettront probablement pas d'obtenir les résultats souhaités, car Google facture la durée de résolution DNS à l'enchérisseur.

La deuxième technique n'assure pas du tout l'équilibrage de charge, car Google sélectionne de manière aléatoire une adresse IP dans la liste de réponses DNS. L'ordre de la réponse n'a donc aucune importance.

Si un enchérisseur modifie un DNS, Google respectera la valeur TTL(Time To Live) définie dans ses enregistrements DNS, mais l'intervalle d'actualisation reste incertain.